DATA MINING: Metascore VideoGames

Studente: Scuderi Ivan

Matricola: 216635

SETUP

Provvedo a definire tutti gli import delle librerie che mi serviranno nel corso della mia analisi, assicurando che il notebook funzioni sia con python 2 che con python 3.

In [1]:
# Python ≥3.5 is required
import sys
assert sys.version_info >= (3, 5)

# Scikit-Learn ≥0.20 is required
import sklearn
assert sklearn.__version__ >= "0.20"

# Common imports
import numpy as np
import os

#imports pandas
import pandas as ps
from IPython.display import Image
import os
#import warnings and Repress Warnings
import warnings
warnings.filterwarnings("ignore", message="numpy.dtype size changed")
warnings.filterwarnings("ignore", message="numpy.ufunc size changed")
warnings.filterwarnings("ignore", message="numpy.ndarray size changed")
warnings.filterwarnings('ignore')

# to make this notebook's output stable across runs
np.random.seed(42)

# To plot pretty figures
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="whitegrid") #White Grid
mpl.rc('axes', labelsize=14)
mpl.rc('xtick', labelsize=12)
mpl.rc('ytick', labelsize=12)

# Where to save the figures
PROJECT_ROOT_DIR = "."
CHAPTER_ID= "ProgettoMetascoreVideogames"
IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, "images")
os.makedirs(IMAGES_PATH, exist_ok=True)

def save_fig(fig_id, tight_layout=True, fig_extension="png", resolution=400):
    path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension)
    print("Saving figure", fig_id)
    if tight_layout:
        plt.tight_layout()
    plt.savefig(path, format=fig_extension, dpi=resolution)
    
def save_figure(figure, fig_id, tight_layout=False, fig_extension="png", resolution=400):
    path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension)
    print("Saving figure", fig_id)
    if tight_layout:
        plt.tight_layout()
    figure.savefig(path, format=fig_extension, dpi=resolution)

# Ignore useless warnings (see SciPy issue #5998)
import warnings
warnings.filterwarnings(action="ignore", message="^internal gelsd")
import warnings
warnings.filterwarnings('ignore')

# Hrule--> "====="
hrule = lambda x : "="*x
Hrule = lambda x,y: "="*(x//2)+y+"="*(x//2)


#Funzioni che possono tornarmi utili nel proseguo 
def take_most_n(n,column):
    return column.value_counts().head(n).index.tolist()

def change_value_to_other(column,top_value,inplace=False):
    if(inplace==True):
        column.where(column.isin(top_value),other='Other',inplace=True)
    else:
        column.where(column.isin(top_value),other='Other',inplace=False)

INTRODUZIONE

In [2]:
Image(filename=os.path.join(IMAGES_PATH, 'cover.jpg'))
Out[2]:

Il mercato videoludico fa parte del più ampio settore dell'intrattenimento, tale particolare "fetta" ha avuto una crescita esponenziale nell'ultimo decennio e si stima che nel corso di quest' anno possa raggiungere un valore complessivo di oltre 160 miliardi di dollari. Intimamente legato a tale ambito è anche il fenomeno degli e-Sport, o "Sport Elettronici", in cui atleti sotto contratto con team professionistici si sfidano in tornei competitivi in diversi giochi e su diverse piattaforme (PC o console).

La mia analisi prende come riferimento un dataset contenente una lista di videogame pubblicati a partire dal 1995 fino al termine del 2018 e cerca di risolvere un problema di regressione in cui ci si concentra sulla previsione del valore di metascore di un gioco, in seguito si daranno informazioni più dettagliate sul significato di tale metrica.

DESCRIZIONE DEL DATASET

In questa prima parte provvedo a caricare il dataset, visualizzandone alcuni record, per poi procedere a descrivere il significato delle singole colonne.

In [3]:
ds = ps.read_csv(os.path.join(PROJECT_ROOT_DIR, "data","game.csv"))
ds.head(5)
Out[3]:
name platform developer publisher genre(s) players rating attribute release_date link critic_positive critic_neutral critic_negative metascore user_positive user_neutral user_negative user_score
0 Command & Conquer PC Westwood Studios Virgin Interactive Sci-Fi 1-4 T NaN Aug 31, 1995 /game/pc/command-conquer 5 0 0 94 47 0 1 8.9
1 Full Throttle PC LucasArts LucasArts Adventure NaN NaN NaN Apr 30, 1995 /game/pc/full-throttle 6 2 0 86 18 1 0 8.7
2 Battle Arena Toshinden PS Tamsoft SCEA Action 1-2 T NaN Sep 9, 1995 /game/playstation/battle-arena-toshinden 1 3 0 69 1 0 1 5.8
3 Sid Meier's Civilization II PC MPS Labs MicroProse Strategy 1 Player K-A NaN Feb 29, 1996 /game/pc/sid-meiers-civilization-ii 7 0 0 94 46 0 1 8.9
4 Quake PC id Software id Software Action 1-16 M NaN Jun 22, 1996 /game/pc/quake 9 0 0 94 84 4 1 8.8

Procedo ad una descrizione dettagliata degli attributi del data set in questione:

  • name - colonna che identifica il nome dello specifico videogioco;
  • platform - piattaforma hardware per cui il titolo è stato sviluppato (es: PC, PS4, GameBoy Advance, GameCube, Xbox, ecc..);
  • developer - azienda di cui fa parte il team di sviluppo che ha realizzato il gioco;
  • publisher - azienda "editrice" del titolo, ossia che ha commissionato ad un team di sviluppo esterno o interno la realizzazione del videogame (developer e publisher non è detto che debbano coincidere);
  • genre(s) - genere/i in cui si può inserire il titolo (es: Action, Adventure, Sports, Simulator, ecc);
  • players - numero di giocatori
  • rating - rating che l'ESRB (Entertainment Software Rating Board) assegna ad un videogame per specificare la fascia d'età a cui questo fa riferimento in base alla presenza o meno di determinate tipologie di contenuti espliciti o violenti, sono presenti 8 valori quali: EC ("Early Childhood", titoli adatti ad età di 3+ anni), E (argomenti adatti ad età di 6+ anni), K-A (che indica "Kids and Adult" ed è stato sostituito da E nel 1998), E10+ (argomenti adatti ad età di 10+ anni), T ("Teen", titoli adatti ad età di 13+ anni), M ("Mature", titoli adatti ad età di 17+ anni), AO ("Adults Only", titoli adatti ad età adulte da 18+ anni) ed infine RP ("Rate Pending", valore che rappresenta che il gioco è ancora in attesa di una valutazione finale sui suoi contenuti e solitamente è impiegato nelle pubblicità di giochi che devono ancora essere rilasciati);
  • attribute - informazioni sul gioco come ad esempio il possibile impiego di supporti hardware aggiuntivi quali strumenti per VR (Virtual Reality);
  • release_data - data (Mese Giorno, Anno) di rilascio del titolo;
  • link - link alla pagine web del titolo;
  • critic_positive/neutral/negative - 3 colonne distinte che rappresentano il numero di recensioni positive/neutrali/negative che il videogame ha ottenuto da parte della critica al lancio, su eventuali gazzette o siti web (non si specifica il voto dei singoli recensori);
  • user_positve/neutral/negative - attributi analoghi ai precedenti ma che ora fanno riferimento al numero di recensioni positive/neutrali/negative che il titolo ha ottenuto da parte degli utenti giocanti;
  • metascore - attributo centrale nella nostra analisi e di cui si vuole predire il valore, ammette valori da 0-100 e rappresenta una valutazione complessiva di un determinato videogame che il portale web "Metacritic" calcola effettuando l'aggregazione di numerose recensioni di critici per lo specifico titolo;
  • userscore - analogo al metascore, viene assegnato dallo stesso portale sopracitato ma questo fa riferimento alle recensioni rilasciate dai vari players ed ammette valori da 0 a 10.

DATA CLEANING

Si procede ora ad analizzare più nello specifico le caratteristiche del dataset andando ad effettuare delle operazioni preliminari di pulizia che ci permetteranno nel proseguo una migliore visualizzazione dei dati in questione.

In [4]:
ds.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20422 entries, 0 to 20421
Data columns (total 18 columns):
 #   Column           Non-Null Count  Dtype 
---  ------           --------------  ----- 
 0   name             20422 non-null  object
 1   platform         20422 non-null  object
 2   developer        20385 non-null  object
 3   publisher        20416 non-null  object
 4   genre(s)         20422 non-null  object
 5   players          13193 non-null  object
 6   rating           18131 non-null  object
 7   attribute        324 non-null    object
 8   release_date     20422 non-null  object
 9   link             20422 non-null  object
 10  critic_positive  20422 non-null  int64 
 11  critic_neutral   20422 non-null  int64 
 12  critic_negative  20422 non-null  int64 
 13  metascore        20422 non-null  int64 
 14  user_positive    20422 non-null  int64 
 15  user_neutral     20422 non-null  int64 
 16  user_negative    20422 non-null  int64 
 17  user_score       20422 non-null  object
dtypes: int64(7), object(11)
memory usage: 2.8+ MB

Come si può ben vedere dal risultato del precedente comando info() il dataset contiene un totale di 18 colonne e 20422 record, tra queste colonne 7 sono di tipo intero e rappresentano i valori numerici delle recensioni effettuate dalla critica e dagli utenti incluso l'attributo principale metascore , mentre le restanti 11 sono object (prevalentemente stringhe). Si può notare come lo user_score fa parte delle colonne di tipo object per via della presenza di valori 'tbd' ('to be defined").

In [5]:
ds.shape
Out[5]:
(20422, 18)

Si procede come prima cosa alla rimozione di eventuiali duplicati all'interno dei nostri dati, si deve però preventivamente motivare la scelta effettuata per tale rimozione.

In [6]:
mask=ds.duplicated(subset='name')
name_duplicate=ds[mask]
name_duplicate.head(6)[2:]
Out[6]:
name platform developer publisher genre(s) players rating attribute release_date link critic_positive critic_neutral critic_negative metascore user_positive user_neutral user_negative user_score
163 Tony Hawk's Pro Skater 2 DC Treyarch Activision Sports NaN T NaN Nov 6, 2000 /game/dreamcast/tony-hawks-pro-skater-2 18 0 0 97 5 0 2 6.1
184 Tony Hawk's Pro Skater 2 PC Neversoft Entertainment Activision Sports 1-4 T NaN Oct 25, 2000 /game/pc/tony-hawks-pro-skater-2 21 1 0 91 7 0 0 8.5
193 Madden NFL 2001 PS EA Sports EA Sports Sports NaN E NaN Aug 22, 2000 /game/playstation/madden-nfl-2001 13 1 0 90 0 0 0 6.8
199 Madden NFL 2001 N64 EA Tiburon EA Sports Sports NaN E NaN Aug 28, 2000 /game/nintendo-64/madden-nfl-2001 6 0 0 89 2 0 0 7.0

Le operazioni effettuate precedentemente mi hanno permesso di selezionare i record che presentano lo stesso nome e che quindi costituiscono lo stesso titolo, nel mondo videoludico è molto frequente che un videogioco venga proposto su diverse piattaforme hardware e inoltre, come si può notare per l'esempio di cui sopra, il metascore del titolo può variare (sensibilmente o meno) in base alla piattaforma per cui è sviluppato. Questo, oltre a sottintendere un certo livello di importanza dell'attributo "platform" nella nostra analisi, giustifica la scelta di non andare ad eliminare record basandomi solo sull'attributo "name".

In [7]:
ds.drop_duplicates(keep='first',inplace=True)
ds.shape
Out[7]:
(20420, 18)

Come si può vedere erano presenti solo 2 elementi duplicati che abbiamo rimosso dal dataset. Si possono a questo punto preliminarmente rimuovere le colonne "name" e "link" dato che non sono significative ai fini della nostra analisi.

In [8]:
ds.drop(columns=['name','link'], inplace = True)
ds.shape
Out[8]:
(20420, 16)

Le colonne rimanenti sono perciò le seguenti:

In [9]:
ds.columns
Out[9]:
Index(['platform', 'developer', 'publisher', 'genre(s)', 'players', 'rating',
       'attribute', 'release_date', 'critic_positive', 'critic_neutral',
       'critic_negative', 'metascore', 'user_positive', 'user_neutral',
       'user_negative', 'user_score'],
      dtype='object')

Si procede ora a gestire la presenza di eventuali valori nulli nel dataset, prima di fare ciò è però necessario andare a sostituire il valore "tbd" di user_score in NaN in modo da avere una visione più chiare di tutti i valori nulli presenti e inoltre permettendoci di rendere tale colonna di tipo float.

In [10]:
ds['user_score']=ds['user_score'].replace('tbd',np.nan)
ds['user_score']=ds['user_score'].astype('float64')
ds.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 20420 entries, 0 to 20421
Data columns (total 16 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   platform         20420 non-null  object 
 1   developer        20383 non-null  object 
 2   publisher        20414 non-null  object 
 3   genre(s)         20420 non-null  object 
 4   players          13193 non-null  object 
 5   rating           18129 non-null  object 
 6   attribute        324 non-null    object 
 7   release_date     20420 non-null  object 
 8   critic_positive  20420 non-null  int64  
 9   critic_neutral   20420 non-null  int64  
 10  critic_negative  20420 non-null  int64  
 11  metascore        20420 non-null  int64  
 12  user_positive    20420 non-null  int64  
 13  user_neutral     20420 non-null  int64  
 14  user_negative    20420 non-null  int64  
 15  user_score       17825 non-null  float64
dtypes: float64(1), int64(7), object(8)
memory usage: 2.6+ MB

Andiamo ora a vedere la percentuale effettiva di valori nulli presenti nel dataset per ogni colonna

In [11]:
ps.DataFrame({'null%':ds.isnull().sum()/ds.shape[0],
              'null_num':ds.isnull().sum()})
Out[11]:
null% null_num
platform 0.000000 0
developer 0.001812 37
publisher 0.000294 6
genre(s) 0.000000 0
players 0.353918 7227
rating 0.112194 2291
attribute 0.984133 20096
release_date 0.000000 0
critic_positive 0.000000 0
critic_neutral 0.000000 0
critic_negative 0.000000 0
metascore 0.000000 0
user_positive 0.000000 0
user_neutral 0.000000 0
user_negative 0.000000 0
user_score 0.127081 2595

Si possono notare diverse colonne con valori nulli, si procede in seguito alla trattazione delle singole colonne e ad eventuali operazioni di conversione.

developer:

Data la presenza di pochi valori nulli, solo 37 che sono molto meno dell'1%, si decide di eliminare i record a cui questi fanno riferimento.

In [12]:
ds.drop(index=ds[ds['developer'].isna()].index, inplace=True)
print("Valori nulli restanti nella colonna 'developer':",ds.developer.isnull().sum())
Valori nulli restanti nella colonna 'developer': 0

publisher:

Come per la colonna precedente anche in questo caso avendo pochissimi valori nulli si è deciso di eliminare i record associati.

In [13]:
ds.drop(index=ds[ds['publisher'].isna()].index, inplace=True)
print("Valori nulli restanti nella colonna 'publisher':",ds.publisher.isnull().sum())
Valori nulli restanti nella colonna 'publisher': 0

players:

Questa colonna contiene oltre 7000 valori nulli che rappresentano il 35% del totale dei valori, data la numerosità di questi si è deciso di andarli a soltituire con la costante "1 or more" poichè comunque sicuramente qualsiasi videogame deve poter permettere ad almeno un giocatore di poter appunto giocare, ma non abbiamo informazioni necessarie per poter stabilire se tali titoli sono single player o meno; inoltre questa scelta, al posto di inserire unicamente "1 Player", ci evita di accorpare record di cui non conosciamo il numero esatto di giocatori con record per i quali si ha solamente un singolo giocatore. A tale scopo si va ad impiegare un oggetto SimpleImputer con strategia 'constant'.

In [14]:
try:
    from sklearn.impute import SimpleImputer # Scikit-Learn 0.20+
except ImportError:
    from sklearn.preprocessing import Imputer as SimpleImputer

imputer_players = SimpleImputer(strategy='constant',fill_value="1 or more",missing_values=np.nan, verbose=0,copy=False)

rating:

Pur avendo relativamente pochi valori nulli in tale colonna si possono con semplicità sostituire con il valore costante RP ossia Rate Pending che, come detto in precedenza, viene impiegato quando appunto non è stato ancora definito il contenuto di un generico videogioco a quale fascia d'età appartiene; per fare ciò si andrà ad impiegare un oggetto SimpleImputer con strategia 'constant', inoltre si provvede a sostituire il valore K-A con E sempre per quanto detto precedentemente nella descrizione degli attributi.

In [15]:
imputer_rating = SimpleImputer(strategy='constant',fill_value="RP",missing_values=np.nan, verbose=0,copy=False)

ds['rating'].replace({"K-A":"E"},inplace=True)
ds.rating.unique()
Out[15]:
array(['T', nan, 'E', 'M', 'E10+', 'AO', 'EC', 'RP'], dtype=object)

attribute:

Siccome quasi tutti i valori di tale colonna sono nulli (più del 98%) non è possibile stabilire una strategia sostitutiva valida perciò si procede con l'eliminazione di tale colonna

In [16]:
ds.drop(columns=['attribute'], inplace = True)
print("Colonne rimanenti:\n",ds.columns)
Colonne rimanenti:
 Index(['platform', 'developer', 'publisher', 'genre(s)', 'players', 'rating',
       'release_date', 'critic_positive', 'critic_neutral', 'critic_negative',
       'metascore', 'user_positive', 'user_neutral', 'user_negative',
       'user_score'],
      dtype='object')

release_date:

Per quanto riguarda tale colonna non è presente alcun "NaN", tuttavia si è deciso di andare ad impiegare unicamente l'anno di pubblicazione del titolo invece della data completa perchè non vi è un'importanza così marcata per la valutazione di un gioco nella conoscenza del suo mese o giorno di pubblicazione, quanto invece può esserci nel sapere l'anno di pubblicazione; inoltre si provvede anche alla conversione della colonna come tipo intero (data la presenza solo dell'anno)

In [17]:
ds['release_date']=ps.to_datetime(ds['release_date'])
ds['release_date']=ds['release_date'].dt.year
ds.rename(columns={"release_date":"year"},inplace=True)
ds.year.unique()
Out[17]:
array([1995, 1996, 1997, 1998, 1999, 2000, 2001, 2018, 2002, 2003, 2004,
       2005, 2017, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
       2015, 2016], dtype=int64)

user_score:

Data la presunta importanza di tale attributo ai fini della previsione e la limitata presenza di valori nulli, che occupano infatti solo il 12% de totale (2595), si procede alla rimozione dei record associati a questi.

In [18]:
ds.drop(index=ds[ds['user_score'].isna()].index, inplace=True)
print("Valori nulli restanti nella colonna 'user_score':",ds.publisher.isnull().sum())
Valori nulli restanti nella colonna 'user_score': 0

Applicazione Imputer:

Andiamo ora ad applicare gli oggetti SimpleImputer precedentemente definiti:

In [19]:
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline

pipe_players=Pipeline([
    ("imputer_players",imputer_players)
])

pipe_rating=Pipeline([
    ("imputer_rating",imputer_rating)
])

pipeline_imputer = ColumnTransformer([
    ("pipe_players",pipe_players,['players']),
    ("pipe_rating",pipe_rating,['rating'])
])

ds1 = pipeline_imputer.fit_transform(ds)

Andiamo ora a mappare le colonne ottenute dalla trasformazione tramite imputer e le restanti colonne che non hanno subito alcuna trasformazione in un dataset.

In [20]:
game=ps.DataFrame({
    "platform":ds.platform,
    "developer":ds.developer,
    "publisher":ds.publisher,
    "genre(s)":ds["genre(s)"],
    "players":ds1[:,0],
    "rating":ds1[:,1],
    "year":ds.year,
    "critic_positive":ds["critic_positive"],
    "critic_neutral":ds["critic_neutral"],
    "critic_negative":ds["critic_negative"],
    "metascore":ds.metascore,
    "user_positive":ds["user_positive"],
    "user_neutral":ds["user_neutral"],
    "user_negative":ds["user_negative"],
    "user_score":ds["user_score"]
})

#game.to_csv(os.path.join(PROJECT_ROOT_DIR, "data","game_clean.csv"))

game.head(5)
Out[20]:
platform developer publisher genre(s) players rating year critic_positive critic_neutral critic_negative metascore user_positive user_neutral user_negative user_score
0 PC Westwood Studios Virgin Interactive Sci-Fi 1-4 T 1995 5 0 0 94 47 0 1 8.9
1 PC LucasArts LucasArts Adventure 1 or more RP 1995 6 2 0 86 18 1 0 8.7
2 PS Tamsoft SCEA Action 1-2 T 1995 1 3 0 69 1 0 1 5.8
3 PC MPS Labs MicroProse Strategy 1 Player E 1996 7 0 0 94 46 0 1 8.9
4 PC id Software id Software Action 1-16 M 1996 9 0 0 94 84 4 1 8.8

A questo punto il dataset è stato ripuliuto da tutti gli eventuali valori mancanti, come si può appunto constatare dal comando sottostante.

In [21]:
ps.DataFrame({'null%':game.isnull().sum()/game.shape[0],
              'null_num':game.isnull().sum()})
Out[21]:
null% null_num
platform 0.0 0
developer 0.0 0
publisher 0.0 0
genre(s) 0.0 0
players 0.0 0
rating 0.0 0
year 0.0 0
critic_positive 0.0 0
critic_neutral 0.0 0
critic_negative 0.0 0
metascore 0.0 0
user_positive 0.0 0
user_neutral 0.0 0
user_negative 0.0 0
user_score 0.0 0

Avendo quindi ripulito i dati si possono andare a presentare delle statistiche generali sulle colonne che presentano valore numerico mediante l'utilizzo del comando describe().

In [22]:
game.describe()
Out[22]:
year critic_positive critic_neutral critic_negative metascore user_positive user_neutral user_negative user_score
count 17796.000000 17796.000000 17796.000000 17796.000000 17796.000000 17796.000000 17796.000000 17796.000000 17796.000000
mean 2010.196730 12.096651 7.605024 1.550236 69.878287 19.322544 5.266858 7.314059 6.947483
std 5.404005 15.617416 7.560908 3.318260 13.232909 76.668148 17.394421 55.513529 1.405150
min 1995.000000 0.000000 0.000000 0.000000 8.000000 0.000000 0.000000 0.000000 0.100000
25% 2006.000000 2.000000 2.000000 0.000000 63.000000 1.000000 0.000000 0.000000 6.300000
50% 2011.000000 6.000000 5.000000 0.000000 72.000000 4.000000 1.000000 1.000000 7.300000
75% 2015.000000 16.000000 10.000000 2.000000 79.000000 12.000000 4.000000 3.000000 7.900000
max 2018.000000 118.000000 60.000000 51.000000 99.000000 3029.000000 507.000000 3001.000000 9.400000

VISUALIZZAZIONE

Dopo aver effettuato una pima serie di operazione di pulizia dei nostri dati concentriamoci ora sulla loro visualizzazione per cercare di individuare i fattori che concorrono al valore di metascore di un videogioco.

Come prima cosa andiamo a mostrare la distribuzione dei valori di metascore nel nostro dataset, si può osservare che occorrono con maggior frequenza i valori compresi tra 70-80. Infatti in seguito si è provveduto a graficare anche l'istogramma relativo al numero di rercord che hanno uno specifico valore di metascore impiegando 20 intervalli.

In [23]:
plt.figure(figsize=(15,10))
p = sns.kdeplot(game.metascore, shade=True, color='blue')
plt.grid()
plt.xlabel('Metascore')
plt.ylabel('Frequency')
plt.title("Metascore Distribution", size=20)
#save_fig("metascore_dist")
plt.show()
In [24]:
plt.figure(figsize=(15,10))
game['metascore'].hist(bins=20)
plt.xlabel('Metascore')
plt.ylabel('count')
plt.title("Metascore values", size=20)
#save_fig("metascore_hist")
plt.show()

Andiamo ora ad analizzare se vi possono essere delle relazioni tra l'attributo year e l'attributo principale metascore: una prima occhiata al grafico sottostante potrebbe indurci a pensare che effettivamente nel corso del tempo la valutazione di un videogioco da parte della critica vada mediamente a diminuire.

In [25]:
plt.figure(figsize=(15,10))
p = sns.lineplot(x = 'year', y = 'metascore', data = game, color='red')
plt.title("metascore changes during years", size =20)
#save_fig("metascore_years_line")
plt.show()

mean_metascore_per_year = game.groupby('year')['metascore'].mean()

plt.figure(figsize=(15,10))
p = sns.barplot(x = mean_metascore_per_year.index, y = mean_metascore_per_year.values)
p = plt.xticks(rotation=90)
plt.title("Mean metascore per year", size=20)
plt.ylabel("mean")
#save_fig("metascore_years_mean")
plt.show()

In realtà andando più a fondo si può dare una spiegazione a questo fenomeno vedendo la numerosità di record campionati prima dell'anno 2000:

In [26]:
game.plot(kind = "scatter", x = "year", y = "metascore" ,color = "r", figsize=(15,10))
plt.xlabel("year")
plt.ylabel("metascore")
plt.title("plotting (year, metascore)", size =20)
#save_fig("metascore_years_scatter")
plt.show()

Si può giustificare lo strano comportamento della curva presa in analisi precedentemente guardando il grafico di cui sopra, il numero di videogiochi presenti nel dataset facenti parte del periodo 1995-1999 è nettamente inferiore rispetto agli anni successivi al 2000 (compreso), perciò tale bassa numerosità ha contribuito a valori medi di metascore molto più elevati per i videogiochi rilasciati in tale periodo. Per il resto non sembrano esserci relazioni rilevanti, come era giusto aspettarsi, tra l'attributo year e l'attributo metascore.

Passiamo ora alla trattazione dell'attributo rating, si può notare dal grafico sottostante che 5 di 7 etichette hanno comportamenti per quanto riguarda la distribuzione dei valori di metascore abbastanza normali se non simili, confermano per l'appunto quanto già visto precedentemente ossia che il range di valori tra 70-80 sono quelli più frequenti. Per quanto riguarda i record con etichette di rating "AO" e "EC" è necessario andare ad analizzare le cause di tali strane figure.

In [27]:
plt.figure(figsize=(15,10))
p = sns.violinplot(x = 'rating', y = 'metascore', data = game)
plt.title("metascore distribution per rating", size=20)
#save_fig("rating_violin")
plt.show()
In [28]:
plt.figure(figsize=(15,10))
p = sns.countplot(x = game.rating)
p = plt.xticks(rotation=90)
plt.title("number of records per rating value", size=20)
#save_fig("rating_count")
plt.show()
In [29]:
print("Record per l'etichetta di rating 'EC':")
game.loc[game.rating=='EC']
Record per l'etichetta di rating 'EC':
Out[29]:
platform developer publisher genre(s) players rating year critic_positive critic_neutral critic_negative metascore user_positive user_neutral user_negative user_score
11747 X360 Microsoft Studios - Soho Productions Microsoft Game Studios Miscellaneous 1 or more EC 2012 4 2 0 76 2 1 0 6.2
In [30]:
print("Record per l'etichetta di rating 'AO':")
game.loc[game.rating=='AO']
Record per l'etichetta di rating 'AO':
Out[30]:
platform developer publisher genre(s) players rating year critic_positive critic_neutral critic_negative metascore user_positive user_neutral user_negative user_score
3885 XBOX Rockstar North Rockstar Games Action Adventure 1-2 AO 2005 58 0 0 93 28 3 0 8.7
15660 PC Destructive Creations Destructive Creations Modern No Online Multiplayer AO 2015 0 21 27 43 42 28 75 4.7

La causa delle anomalie nel "violinplot" precedente, come si poteva immaginare, sono dovute alla bassa numerosità di record che rappresentano videogiochi etichettati come "EC" oppure "AO" nel nostro dataset, rispettivamente 1 e 2 soli record, mentre si decide di non andare a modificare gli elementi appartenenti alla categoria di rating "AO", uno studio più approfondito del contesto mi porta alla scelta di accorpare il singolo elemento etichettato "EC" con i restanti record di rating "E"; questo perche effettivamente risulta essere una categoria passata in disuso nel tempo.

In [31]:
game['rating']=game['rating'].replace('EC','E')
#game.to_csv(os.path.join(PROJECT_ROOT_DIR, "data","game_clean.csv"))

print("Numero di record con etichetta di rating 'EC': ",(game.rating=='EC').sum())
print("Numero di record con etichetta di rating 'AO': ",(game.rating=='AO').sum())
Numero di record con etichetta di rating 'EC':  0
Numero di record con etichetta di rating 'AO':  2

Si prosegue effettuando la visualizzazione dei generi di videogame, in particolare si va a graficare tramite istogramma il numero di titoli che fanno parte di ogni genere:

In [32]:
plt.figure(figsize=(15,10))
p = sns.countplot(x = 'genre(s)', data = game)
p = plt.xticks(rotation=90)
plt.title("number of games per genre(s)", size=20)
#save_fig("genre_count")
plt.show()

Si può notare come videogiochi di genere Action siano quelli maggiormente presenti nel nostro dataset, inoltre escludendo insieme a questo altri 6 generi che riescono a raggiungere, o superare, la soglia dei 1000 titoli, gli altri sono presenti solo in minima quantità. In particolare si osserva il genere Other:

In [33]:
print("Numero di record con genere 'Other': ",(game['genre(s)']=='Other').sum())
Numero di record con genere 'Other':  1

Data la presenza di un solo titolo, per eventuali trasformazioni future applicabili al dataset nella fase di preparazione per gli algoritmi di analisi, si sceglie di conservare tale record. Inoltre dal grafico sottostante non si evince una dominanza particolare di valori elevati di metascore per determinate tipologie di generi videoludici, tralasciando 5 generi che superano la soglia di 80 gli atri si assestano nella maggior parte dei casi nel range 60-70; ciò che si può invece notare è come per molti di questi viene campionato il valore medio con la stima dell'errore che si compie mentre per altri l'errore risulta nullo. Anche questo fenomeno quasi sicuramente sarà dovuto alla bassa numerosità di elementi per queste categorie che potrebbero essere proprio 1 (come per Other), oppure alla presenza di record per la stessa categoria con il medesimo valore di metascore. Inoltre è probabile che l'elevata numerosità di determinate tipologie di generi videoludici vada ad influire negativamente sul metascore medio, come si può osservare ad esempio per il genere Action.

In [34]:
plt.figure(figsize=(15,10))
p = sns.barplot(x = 'genre(s)', y = 'metascore', data = game)
p = plt.xticks(rotation=90)
plt.title("metascore based on genre", size=20)
#save_fig("metascore_genre_barplot")
plt.show()

Indagando più nello specifico risulta vera la prima affermazione, in effetti effettuando un controllo delle colonne del grafico di cui sopra e dei valori di genre(s) sottostanti, che per l'appunto hanno un solo record, si riesce a vedere come l'errore è nullo proprio in corrispondeza di questi.

In [35]:
s=(game['genre(s)'].value_counts()==1)
index = s.index 
for genre in index[s]:
    print("Numero di record con genere '",genre,"': ",(game['genre(s)']==genre).sum())
Numero di record con genere ' 4X ':  1
Numero di record con genere ' Flight ':  1
Numero di record con genere ' Turn-Based ':  1
Numero di record con genere ' Music Maker ':  1
Numero di record con genere ' Wargame ':  1
Numero di record con genere ' Formula One ':  1
Numero di record con genere ' Street ':  1
Numero di record con genere ' Real-Time ':  1
Numero di record con genere ' Edutainment ':  1
Numero di record con genere ' Board Games ':  1
Numero di record con genere ' Other ':  1
Numero di record con genere ' Rail ':  1
Numero di record con genere ' MOBA ':  1
Numero di record con genere ' Western-Style ':  1
Numero di record con genere ' WWII ':  1
Numero di record con genere ' WWI ':  1
Numero di record con genere ' Olympic Sports ':  1
Numero di record con genere ' Tactical ':  1
Numero di record con genere ' Virtual Life ':  1
Numero di record con genere ' Breeding/Constructing ':  1

Analizziamo ora cosa varia se si decidono di accorpare in Other tutti i generi che presentano pochi valori mantenedo i 10 più numerosi

In [36]:
game2=game.copy()
most_n_genre = take_most_n(10,game2['genre(s)'])
change_value_to_other(game2['genre(s)'],most_n_genre,inplace=True)

fig, ax = plt.subplots(figsize=(20, 15), subplot_kw=dict(aspect="equal"))

def func(pct, allvals):
    absolute = int(pct/100.*np.sum(allvals))
    return "{:.1f}%\n({:d})".format(pct, absolute)

genres = game2['genre(s)'].value_counts().index.tolist()

data = game2['genre(s)'].value_counts().values.tolist()
data2 = [float(x) for x in data]


wedges, texts, autotexts = ax.pie(game2['genre(s)'].value_counts(), autopct=lambda pct: func(pct, data2), shadow=True)

ax.legend(wedges, genres,
          title="genres",
          loc="center left",
          bbox_to_anchor=(1, 0, 0.5, 1))

plt.setp(autotexts, size=13, weight="bold")

ax.set_title("top genres pie chart",size=20)
#save_fig("pie_chart_genre")
plt.show()
In [37]:
plt.figure(figsize=(15,10))
p = sns.barplot(x = 'genre(s)', y = 'metascore',data=game2)
p = plt.xticks(rotation=90)
plt.title("mean metascore per genre", size=20)
#save_fig("mean_metascore_genre")
plt.show()

Si nota come il 4.6% di record, ossia un totale di 817, siano stati inseriti nel genere Other andando così ad accorpare circa 55 generi differenti, per l'appunto si può vedere come tale accorpamento sia andato a far diminuire il valore di metascore medio per Other rispetto al valore precedentemente osservato che si attestava intorno a 94

In [38]:
plt.figure(figsize=(15,10))
p = sns.stripplot(x = 'platform',y='metascore',data=game)
plt.title("platforms titles metascore", size=20)
#save_fig("stripplot_platform")
plt.show()
In [39]:
plt.figure(figsize=(15,10))
p = sns.barplot(x = 'platform', y = 'metascore', data = game)
plt.title("metascore based on platform", size=20)
#save_fig("metascore_platform_barplot")
plt.show()

Per quanto riguarda invece la piattaforma hardware per cui vengono sviluppati i diversi videogiochi si può notare una distribuzione abbastanza simile dei valori di metascore, con buona confidenza possiamo affermare che non sarà un attributo preponderante nella valutazione di un gioco, infatti è logico supporre che il metro di giudizio della critica o del generico player non sia condizionato dallo strumento di gioco ma bensì cambi al variare di questo, non sarebbe equo andare ad assegnare valutazioni più elevate a titoli sviluppati per hardware più recenti e quindi con migliori capacità dal punto di vista di elaborazione grafica e performance. Si considera inoltre il fatto che le valutazioni dei diversi titoli siano state fatte in concomitanza con la loro uscita sul mercato, per questo motivo la bontà o meno di un videogame sviluppato per console "vecchie" non pregiudica quella di videogame sviluppati per hardware "nuovi".

Si prosegue l'analisi andando a prendere in considerazione l'attributo publisher che rappresenta la "casa produttrice" del videogame. Si osserva un elevato numero di valori assunti da tale colonna:

In [40]:
print("Numero di publisher: ",len(list(game['publisher'].unique())))
Numero di publisher:  1923

Si procede perciò, come precedentemente per l'attributo genres, all'aggregazione di tali valori mantenendo i più presenti all'interno del dataset.

In [41]:
game3=game.copy()

top_publisher=100
most_n_publisher = take_most_n(top_publisher,game3['publisher'])
change_value_to_other(game3['publisher'],most_n_publisher,inplace=True)

plt.figure(figsize=(15,10))
p = sns.countplot(x = game3.publisher)
p = plt.xticks(rotation=90)
plt.title("number of games for "+str(top_publisher)+" top publisher", size=20)
#save_fig("top_publisher_count")
plt.show()

plt.figure(figsize=(15,10))
p = sns.barplot(x = game3.publisher, y=game3.metascore)
p = plt.xticks(rotation=90)
plt.title("mean metascore for "+str(top_publisher)+" top publisher", size=20)
#save_fig("top_publisher_metascore")
plt.show()

Analizzando il primo grafico si può notare come in questo caso l'aggregazione in Other dei valori di publisher meno presenti ha fatto si che tale "categoria" diventi quella con la numerosità più elevata all'interno del dataset, vedendo come nessuno dei publisher più frequenti riesca a raggiungere la soglia dei 1000 record si può supporre che dei 1923 valori differenti di publisher, i record associati ad ognuno di essi siano in numero limitato. Non vi è perciò un gruppo di "case editrici" di videogames che si distingue rispetto alle altre per numero di giochi prodotti. Inoltre dal secondo grafico si può osservare come i publisher "Blizzard Entertainment" e "Rockstar Games" si discostano dagli altri essendo gli unici i cui titoli mediamente riescano a superare la valutazione di 80 per il metascore, questo però per quanto osservato precedentemente può essere dipeso anche dalla numerosità dei loro record.

Passiamo ora alla visualizzazione degli attributi numerici (escluso year) di cui è costituito il dataset mediante l'utilizzo di Pairplot e concentrandoci principalmente sull'attributo metascore

In [42]:
columns=['critic_positive','critic_neutral','critic_negative','metascore','user_positive','user_neutral','user_negative','user_score']

plt.figure(figsize=(15,10))
sns.pairplot(game[columns],diag_kind="kde")
#save_fig("pairplot")
plt.show()
<Figure size 1080x720 with 0 Axes>

Analizzando la colonna di grafici relativa al metascore si può vedere una tendenza a valori più elevati in corrispondenza di elevati valori di user_score e critic_positive, maggiormente marcata per quest'ultimo attributo, mentre per quanto riguarda critic_negative si osserva il comportamento opposto ossia record con una valutazione di metascore inferiore tendono ad avere anche il maggior numero di recensioni negative da parte della critica. Indaghiamo più nello specifico le relazioni che vi sono tra gli attributi appena citati.

In [43]:
plt.figure(figsize=(15,10))
sns.regplot( x = "user_score", y = "metascore" ,data=game)
plt.xlabel("user score")
plt.ylabel("metascore")
plt.title("plotting (user_score, metascore)", size =20)
#save_fig("userscore_metascore")
plt.show()
In [44]:
plt.figure(figsize=(20,10))
p = sns.jointplot(x = 'user_score', y = 'metascore',kind='hex', data = game)
plt.title("relation between metascore and user_score", size =15)
#save_fig("userscore_metascore_jointplot")
plt.show()
<Figure size 1440x720 with 0 Axes>

Dai grafici soprastanti si può vedere come la distribuzione dei valori di metascore e user_score sono relativamente simili, inoltre i record con valutazione da parte degli utenti maggiore tendono anche ad ottenere un' elevata valutazione da parte della critica.

In [45]:
plt.figure(figsize=(15,10))
sns.regplot( x = "critic_negative", y = "metascore" ,data=game,color='g')
plt.xlabel("critic_negative")
plt.ylabel("metascore")
plt.title("plotting (critic_negative, metascore)", size =20)
#save_fig("criticnegative_metascore")
plt.show()
In [46]:
plt.figure(figsize=(20,10))
p = sns.jointplot(x = 'critic_negative', y = 'metascore', data = game,color='g',kind='hex')
plt.title("relation between critic_negative and metascore", size =15)
#save_fig("criticnegative_metascore_jointplot")
plt.show()
<Figure size 1440x720 with 0 Axes>
In [47]:
plt.figure(figsize=(15,10))
p = sns.kdeplot(game['critic_negative'], shade=True, color='green')
plt.grid()
plt.xlabel('Number of Negative Reviews from Critics')
plt.ylabel('Frequency')
plt.title("Critic Negative Distribution", size=20)
#save_fig("critic_negative_dist")
plt.show()

Per quanto riguarda l'attributo critic_negative si può vedere come la tendenza dei videogiochi con poche recensioni negative è quella ad avere un valore di metascore abbastanza elevato, anche se si nota come la frequenza maggiore la hanno i record con un numero di recensioni negative da parte della critica comprese tra 1 e 5.

In [48]:
plt.figure(figsize=(15,10))
sns.regplot( x = "critic_positive", y = "metascore" , data=game, color='c')
plt.xlabel("critic_positive")
plt.ylabel("metascore")
plt.title("plotting (critic_positive, metascore)", size =20)
#save_fig("criticpositive_metascore")
plt.show()
In [49]:
plt.figure(figsize=(20,10))
p = sns.jointplot(x = 'critic_positive', y = 'metascore', data = game,color='c',kind='hex')
plt.title("relation between critic_positive and metascore", size =15)
#save_fig("criticpositive_metascore_jointplot")
plt.show()
<Figure size 1440x720 with 0 Axes>
In [50]:
plt.figure(figsize=(15,10))
p = sns.kdeplot(game['critic_positive'], shade=True, color='c')
plt.grid()
plt.xlabel('Number of Positive Reviews from Critics')
plt.ylabel('Frequency')
plt.title("Critic Positive Distribution", size=20)
#save_fig("critic_positive_dist")
plt.show()

Inoltre, come ci si poteva aspettare, per i videogiochi che ottengono un numero elevato di recensioni positive da parte della critica si osserva anche un valore di metascore con buona probabilità elevato e superiore a 60. Si nota, similmente all'attributo precedentemente trattato, che anche per critic_positive la distribuzione dei valori si concentra maggiormente tra 0 e 10.

Si va ora a mostrare la matrice di correlazione calcolata su tali attributi numerici per indagare la presenza di eventuali correlazioni lineari positive o negative facendo ovviamente particolare attenzione all'attributo metascore.

In [51]:
fig,ax = plt.subplots(figsize=(15,10))
fig = sns.heatmap(game.corr(), annot=True)
plt.title("Correlations Matrix", size=20)
#save_fig("corr_matrix")
plt.show()

Effettivamente si può notare come il coefficiente di Pearson calcolato tra metascore-user_score e tra metascore-critic_positive restituisca valori superiori a 0.50 e quindi ci indica una bassa, seppur presente, correlazione positiva tra tali attributi, mentre considerando la coppia metascore-critic_negative si ottiene un valore pari a circa -0.65 che quindi sottintende alla presenza di correlazione negativa tra i due attributi. I restanti attributi non sembrano sottolineare la presenza di alcuna tipologia di correlazione lineare con metascore.

Si procede ora alla generazione di un dataframe che contenga come colonne le somme delle recensioni positive, neutrali e negative effettuate da utenti e critici per indagare se, eventuali attributi aggiuntivi non presenti nel dataset originale possano portare a scoprire relazioni incognite con l'attributo metascore.

In [52]:
columns_sum =  ps.DataFrame({'positive_rec':game['critic_positive']+game['user_positive'],
                  'neutral_rec':game['critic_neutral']+game['user_neutral'],
                  'negative_rec':game['critic_negative']+game['user_negative'],
                  'metascore':game['metascore']})
columns_sum.head(10)
Out[52]:
positive_rec neutral_rec negative_rec metascore
0 52 0 1 94
1 24 3 0 86
2 2 3 1 69
3 53 0 1 94
4 93 4 1 94
5 96 8 7 94
6 233 14 10 94
7 12 1 1 93
8 27 3 0 92
9 44 4 1 91

Una volta ottenuto per costruzione tale dataframe si procede a graficare la matrice di correlazione per questi nuovi attributi.

In [53]:
fig,ax = plt.subplots(figsize=(15,10))
fig = sns.heatmap(columns_sum.corr(), annot=True)
plt.title("Correlations Matrix: new columns", size=20)
plt.show()

La strada percorsa non porta un miglioramente nel calcolo del coefficiente di Pearson, anzi i valori tendono ad essere inferiori. Perciò si decide di non impiegare tali colonne nel proseguo dell'analisi.

Si termina la fase di Visualizzazione andando ad applicare sulle colonne del dataset un oggetto LabelEncoder per poter impiegare un RandomForestRegressor al fine di visualizzare quali attributi hanno una maggiore influenza nella previsione del metascore di un videogame; si può notare dal grafico sottostante come critic_positive, critic_negative e critic_neutral siano centrali per l'ottenimento del valore di metascore.

In [54]:
from sklearn.preprocessing import LabelEncoder
from sklearn.ensemble import RandomForestRegressor

Encoder = LabelEncoder() 
data_enc = game.copy()

for c in data_enc.columns:
    data_enc[c] = Encoder.fit_transform(data_enc[c])

x = data_enc.drop('metascore', axis = 1)
y = data_enc['metascore']

rfr = RandomForestRegressor(n_estimators=40, n_jobs=-1, random_state=40)
rfr.fit(x, y)
attributes = x.columns
importances = rfr.feature_importances_
index = np.argsort(importances)

plt.figure(figsize=(15,10))
plt.title("Attribute importance for the estimation of metascore",size=15)
p = plt.barh(range(len(index)), importances[index], color='g', align='center')
plt.yticks(range(len(index)), attributes[index])
plt.xlabel("Relative importance")
plt.show()
# save_fig("Attribute importance")

TRASFORMAZIONE DATASET

In questa fase, siccome le operazioni di data cleaning dei dati per l'eliminazione di eventuali valori nulli sono state effettuate a monte, si andranno ad impiegare oggetti Transformers facenti parte della libreria sklearn.preprocessing per effuettuare appunto le dovute trasformazioni degli attributi del dataset, principalmente per favorire una migliore applicazione degli algoritmi di machine learning che si andranno ad utilizzare nelle fasi successive del progetto. Come prima cosa andiamo ad escludere dal dataset la colonna relativa a metascore dalle restanti.

In [55]:
game_tr = game.copy()

ydf = game_tr.loc[:,['metascore']]
game_tr.drop(columns=['metascore'], inplace = True)
xdf = game_tr
In [56]:
xdf.head(5)
Out[56]:
platform developer publisher genre(s) players rating year critic_positive critic_neutral critic_negative user_positive user_neutral user_negative user_score
0 PC Westwood Studios Virgin Interactive Sci-Fi 1-4 T 1995 5 0 0 47 0 1 8.9
1 PC LucasArts LucasArts Adventure 1 or more RP 1995 6 2 0 18 1 0 8.7
2 PS Tamsoft SCEA Action 1-2 T 1995 1 3 0 1 0 1 5.8
3 PC MPS Labs MicroProse Strategy 1 Player E 1996 7 0 0 46 0 1 8.9
4 PC id Software id Software Action 1-16 M 1996 9 0 0 84 4 1 8.8
In [57]:
ydf.head(5)
Out[57]:
metascore
0 94
1 86
2 69
3 94
4 94

Procediamo ora nella gestione di ogni singola colonna andando ad analizzare le trasformazioni che si applicheranno sui dati.

critic_positive, critic_negative, critic_neutral, user_score:

Per tali attributi si procede con la loro standardizzazione tramite l'applicazione di uno StandardScaler, questo perchè essendo gli attributi con la maggiore rilevanza per la stima del metascore, impiegando tale strategia di scaling si andrà ad ottenere una distribuzione di valori simile alla Normale Gaussiana (centrata intorno alla media), in modo da favorire maggiormente l'applicazione futura degli algoritmi di machine learning.

In [58]:
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

critics_uscore_reviews = ['critic_positive','critic_neutral','critic_negative','user_score']

std_scaler = StandardScaler()

pipe_critics_uscore = Pipeline([
    ('std_scaler', std_scaler)
])

user_positive, user_negative, user_neutral:

Per quanto riguarda gli attributi relativi alle recensioni rilasciate dagli utenti si possono notare dei valori massimi abbastanza elevati per tali colonne, così come è elevata anche la loro deviazione standard.

In [59]:
users_reviews = ['user_positive','user_neutral','user_negative']
game[users_reviews].describe()
Out[59]:
user_positive user_neutral user_negative
count 17796.000000 17796.000000 17796.000000
mean 19.322544 5.266858 7.314059
std 76.668148 17.394421 55.513529
min 0.000000 0.000000 0.000000
25% 1.000000 0.000000 0.000000
50% 4.000000 1.000000 1.000000
75% 12.000000 4.000000 3.000000
max 3029.000000 507.000000 3001.000000

La scelta della stategia di standardizzazione per tali valori è ricaduta sull'impiego di un RobustScaler, si vanno perciò a centrare tali valori attorno alla mediana, e potenzialmente si andrà ad ottenere un risultato migliore dall'applicazione degli estimators perchè tale scaler è appunto robusto rispetto alla presenza di eventuali outliers.

In [60]:
from sklearn.preprocessing import RobustScaler

rbst_scaler = RobustScaler()

pipe_users = Pipeline([
    ('rbst_scaler', rbst_scaler)
])

year:

La colonna in questione verrà come prima cosa discretizzata in intervalli con un numero di elementi uniformi, siccome abbiamo visto nella fase precedente che non è molto influente nella decisione del metascore e quanti pochi record siano presenti nel range di anni 1995-2000; si andranno inoltre a codificare tali intervalli seguendo una strategia di One Hot Encoding. Tutto questo può essere svolto tramite l'utilizzo di un KBinsDiscretizer.

In [61]:
from sklearn.preprocessing import KBinsDiscretizer

discr_year = KBinsDiscretizer(n_bins=5, encode='onehot',strategy = 'uniform')

pipe_year = Pipeline([
    ('discr_year', discr_year)
])

rating:

Per l'attributo rating si procede all'applicazione di encoding tramite OneHotEncoder.

In [62]:
from sklearn.preprocessing import OneHotEncoder

rating_enc = OneHotEncoder(sparse=False)

pipe_rating = Pipeline([
    ('rating_enc', rating_enc)
])

genre(s), platform:

Si procede all'aggregazione dei valori di tali colonne mantenendo per genre(s) i 10 più presenti insieme ad "Other", per poi procedere all'encoding di entrambe tramite OneHotEncoder.

In [63]:
most_n_genre = take_most_n(10,xdf['genre(s)'])
change_value_to_other(xdf['genre(s)'],most_n_genre,inplace=True)

xdf['genre(s)'].unique()
Out[63]:
array(['Other', 'Adventure', 'Action', 'Strategy', 'Role-Playing',
       'Driving', 'Action Adventure', 'Miscellaneous', 'Simulation',
       'Sports', 'General'], dtype=object)
In [64]:
genres_plat_col = ['genre(s)','platform']

genres_plat_enc = OneHotEncoder(sparse=False)

pipe_genres_plat = Pipeline([
    ('genres_plat_enc', genres_plat_enc)
])

players:

Data la varietà di valori presente in tale colonna, non si riesce ad ottenere un valido metodo di aggregazione, nonostante ciò la loro numerosità non è così elevata da creare problemi sulla dimensionalità del dataset, perciò si procede anche in questo caso all'applicazione di encoding tramite OneHotEncoder.

In [65]:
players_enc = OneHotEncoder(sparse=False)

pipe_players = Pipeline([
    ('players_enc', players_enc)
])

developer, publisher:

Data l'elevata numerosità di valori presenti in tali attributi, una trasformazione basata su one hot encoding risulterebbe nell'inserimento di moltissime nuove colonne con valori assunti 0 o 1, questo potrebbe causare problemi nell'applicazione di algoritmi di machine learning per via dell'elevata dimensionalità del dataset. La scelta perciò è ricaduta su una strategia di encoding basata su OrdinalEncoder.

In [66]:
print("Numero di valori assunti dalla colonna 'developer': ",len(list(game['developer'].unique())))
print("Numero di valori assunti dalla colonna 'publisher': ",len(list(game['publisher'].unique())))
print("Numero di valori totali assunti dalle due colonne: ",len(list(game['publisher'].unique()))+len(list(game['developer'].unique())))
Numero di valori assunti dalla colonna 'developer':  4154
Numero di valori assunti dalla colonna 'publisher':  1923
Numero di valori totali assunti dalle due colonne:  6077
In [67]:
from sklearn.preprocessing import OrdinalEncoder

dev_publ_col = ['developer', 'publisher']

ordinal_enc = OrdinalEncoder()

pipe_dev_publ = Pipeline([
    ('dev_publ_enc', ordinal_enc)
])

Si procede ora con l'applicazione di tutte le Pipeline definite sul dataset mediante l'utilizzo di un oggetto ColumnTransformer.

In [68]:
from sklearn.compose import ColumnTransformer

full_pipeline = ColumnTransformer([
    
    ('pipe_critics_uscore', pipe_critics_uscore, critics_uscore_reviews),
    ('pipe_users', pipe_users, users_reviews),
    ('pipe_year', pipe_year, ['year']),
    ('pipe_rating', pipe_rating, ['rating']),
    ('pipe_genres_plat', pipe_genres_plat, genres_plat_col),
    ('pipe_players', pipe_players, ['players']),
    ('pipe_dev_publ', pipe_dev_publ, dev_publ_col)
])

X, Y = full_pipeline.fit_transform(xdf), ydf.values.reshape(-1)
In [69]:
print(X)
print(hrule(40))
print(Y)
[[-4.54418999e-01 -1.00586292e+00 -4.67196504e-01 ...  0.00000000e+00
   3.96200000e+03  1.76900000e+03]
 [-3.90386119e-01 -7.41337003e-01 -4.67196504e-01 ...  0.00000000e+00
   2.03400000e+03  9.63000000e+02]
 [-7.10550520e-01 -6.09074044e-01 -4.67196504e-01 ...  0.00000000e+00
   3.51100000e+03  1.41000000e+03]
 ...
 [-7.74583401e-01 -8.73599961e-01  2.84788683e+00 ...  0.00000000e+00
   3.43100000e+03  1.57500000e+03]
 [-7.10550520e-01 -1.00586292e+00  5.56022773e+00 ...  0.00000000e+00
   3.35400000e+03  1.54500000e+03]
 [-7.74583401e-01 -1.00586292e+00  7.38288343e-01 ...  0.00000000e+00
   8.71000000e+02  3.90000000e+02]]
========================================
[94 86 69 ... 29 28 28]

APPLICAZIONE ALGORITMI DI ML

Una volta terminata la fase di trasformazione del dataset, preceduta a monte da operazioni di datacleaning e conversione dei dati, si può procedere all'addestramento degli algoritmi di machine learning per risolvere il problema di previsione del valore di metascore per un dato videogame, come prima cosa si impiegano gli strumenti forniti dalla libreria sklearn per l'ottenimento di un test_set e di un train_set a partire dal dataset completo; avendo applicato le operazioni di trasformazione a tutti i record abbiamo il test_set già pronto per l'applicazione dei futuri modelli di previsione addestrati. Inoltre si sceglie l'utilizzo delle metriche di valutazione dei modelli: 'root mean squared error'(errore quadratico medio), 'mean absolute error' (errore medio assoluto), 'r2', 'explained variance'.

In [70]:
from sklearn.model_selection import train_test_split

X_train, X_test,  y_train, y_test = train_test_split(X,Y, test_size=0.2)
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
(14236, 104) (14236,) (3560, 104) (3560,)
In [71]:
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import GridSearchCV
import pickle

#metriche impiegate: root mean squared error, mean absolute error, r2, explained variance

metriche = ['neg_mean_squared_error', 'neg_mean_absolute_error', 'r2', 'explained_variance']

#metodo che passandogli un modello calcola le metriche in questione tramite l'impiego di cv e restituisce i valori 
#come dataframe

ps.options.display.float_format = '{:.8f}'.format

def calcola_metriche(modello, nome, X, y):
    elem = ps.DataFrame(index={0},columns=['nome','rmse', 'mae', 'r2', 'exp_var'])
    elem['nome']=nome
    for i in metriche:
        if(i=='neg_mean_squared_error'):
            score = cross_val_score(modello, X ,y, cv=10 , n_jobs=-1, scoring='neg_mean_squared_error').mean()
            elem['rmse'] = np.sqrt(-score)
            
        if(i=='neg_mean_absolute_error'):
            score = cross_val_score(modello, X ,y, cv=10 , n_jobs=-1, scoring='neg_mean_absolute_error').mean()
            elem['mae'] = -score
            
        if(i=='r2'):
            score = cross_val_score(modello, X ,y, cv=10 , n_jobs=-1, scoring='r2').mean()
            elem['r2'] = score
            
        if(i=='explained_variance'):
            score = cross_val_score(modello, X ,y, cv=10 , n_jobs=-1, scoring='explained_variance').mean()
            elem['exp_var'] = score
            
    return elem

MODELS_PATH = os.path.join(PROJECT_ROOT_DIR, "models")
RESULTS_PATH = os.path.join(PROJECT_ROOT_DIR, "results")

modelli = []
nomi = []
risultati = ps.DataFrame()

Modelli di Base

Modelli Lineari

In [72]:
modelli_lineari = []
nomi_modelli_lineari = []

Linear Regressor:

In [73]:
from sklearn.linear_model import LinearRegression

#lr = LinearRegression()
#lr.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "linear_regressor.sav")
#pickle.dump(lr, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "linear_regressor.sav")
lr = pickle.load(open(filename, 'rb'))
print(lr)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [74]:
#r = calcola_metriche(lr, 'Linear Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "linear_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "linear_regressor_res.csv"), index_col='nome')
r
Out[74]:
rmse mae r2 exp_var
nome
Linear Regressor 7.40186339 5.37135068 0.68366814 0.68388143
In [75]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [76]:
modelli.append(lr)
modelli_lineari.append(lr)
nomi.append("Linear Regressor")
nomi_modelli_lineari.append("Linear Regressor")

Ridge Regressor:

In [77]:
from sklearn.linear_model import Ridge

#ridge = Ridge()
#ridge.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "ridge_regressor.sav")
#pickle.dump(ridge, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "ridge_regressor.sav")
ridge = pickle.load(open(filename, 'rb'))
print(ridge)
Ridge(alpha=1.0, copy_X=True, fit_intercept=True, max_iter=None,
      normalize=False, random_state=None, solver='auto', tol=0.001)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [78]:
#r = calcola_metriche(ridge, 'Ridge Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "ridge_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "ridge_regressor_res.csv"), index_col='nome')
r
Out[78]:
rmse mae r2 exp_var
nome
Ridge Regressor 7.39787930 5.36856111 0.68400897 0.68421854
In [79]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [80]:
modelli.append(ridge)
modelli_lineari.append(ridge)
nomi.append("Ridge Regressor")
nomi_modelli_lineari.append("Ridge Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [81]:
#params={
#    'alpha': [25,10,4,2,1.0,0.8,0.5,0.3,0.2,0.1]
#}

#ridge_grid_search = GridSearchCV(ridge, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1)
#ridge_grid_search.fit(X_train, y_train)
#best_ridge = ridge_grid_search.best_estimator_
#best_ridge

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "ridge_regressor_tuned.sav")
#pickle.dump(best_ridge, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "ridge_regressor_tuned.sav")
best_ridge = pickle.load(open(filename, 'rb'))
print(best_ridge)
Ridge(alpha=25, copy_X=True, fit_intercept=True, max_iter=None, normalize=False,
      random_state=None, solver='auto', tol=0.001)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [82]:
#r = calcola_metriche(best_ridge, 'Ridge Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "ridge_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "ridge_regressor_tuned_res.csv"), index_col='nome')
r
Out[82]:
rmse mae r2 exp_var
nome
Ridge Regressor Tuned 7.38926769 5.35861469 0.68474985 0.68494719
In [83]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [84]:
modelli.append(best_ridge)
modelli_lineari.append(best_ridge)
nomi.append("Ridge Regressor Tuned")
nomi_modelli_lineari.append("Ridge Regressor Tuned")

Stochastic Gradient Descent Regressor:

In [85]:
from sklearn.linear_model import SGDRegressor

#sgd_reg = SGDRegressor(random_state=42)
#sgd_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "sgd_regressor.sav")
#pickle.dump(sgd_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "sgd_regressor.sav")
sgd_reg = pickle.load(open(filename, 'rb'))
print(sgd_reg)
SGDRegressor(alpha=0.0001, average=False, early_stopping=False, epsilon=0.1,
             eta0=0.01, fit_intercept=True, l1_ratio=0.15,
             learning_rate='invscaling', loss='squared_loss', max_iter=1000,
             n_iter_no_change=5, penalty='l2', power_t=0.25, random_state=42,
             shuffle=True, tol=0.001, validation_fraction=0.1, verbose=0,
             warm_start=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [86]:
#r = calcola_metriche(sgd_reg, 'SGD Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "sgd_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "sgd_regressor_res.csv"), index_col='nome')
r
Out[86]:
rmse mae r2 exp_var
nome
SGD Regressor 698634553618519.62500000 495593827541853.87500000 -2864227922721051520127729664.00000000 -907476207591652740059627520.00000000
In [87]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [88]:
modelli.append(sgd_reg)
modelli_lineari.append(sgd_reg)
nomi.append("SGD Regressor")
nomi_modelli_lineari.append("SGD Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [89]:
#params = {
#    'loss': ['squared_loss', 'huber', 'epsilon_insensitive', 'squared_epsilon_insensitive'],
#    'penalty': ['l2','elasticnet', 'l1'],
#    'alpha' : [0.0001, 0.0002, 0.001, 0.005, 0.02, 0.1, 0.2],
#    'shuffle' : [True, False]
#}

#sgd_grid_search = GridSearchCV(sgd_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1)
#sgd_grid_search.fit(X_train, y_train)
#best_sgd = sgd_grid_search.best_estimator_
#best_sgd

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "sgd_regressor_tuned.sav")
#pickle.dump(best_sgd, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "sgd_regressor_tuned.sav")
best_sgd = pickle.load(open(filename, 'rb'))
print(best_sgd)
SGDRegressor(alpha=0.0001, average=False, early_stopping=False, epsilon=0.1,
             eta0=0.01, fit_intercept=True, l1_ratio=0.15,
             learning_rate='invscaling', loss='huber', max_iter=1000,
             n_iter_no_change=5, penalty='l1', power_t=0.25, random_state=42,
             shuffle=True, tol=0.001, validation_fraction=0.1, verbose=0,
             warm_start=False)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [90]:
#r = calcola_metriche(best_sgd, 'SGD Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "sgd_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "sgd_regressor_tuned_res.csv"), index_col='nome')
r
Out[90]:
rmse mae r2 exp_var
nome
SGD Regressor Tuned 141.97593276 99.75022096 -114.81395536 -28.13841491
In [91]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [92]:
modelli.append(best_sgd)
modelli_lineari.append(best_sgd)
nomi.append("SGD Regressor Tuned")
nomi_modelli_lineari.append("SGD Regressor Tuned")

ElasticNet Regressor:

In [93]:
from sklearn.linear_model import ElasticNet

#eln_reg = ElasticNet(random_state=42)
#eln_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "eln_regressor.sav")
#pickle.dump(eln_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "eln_regressor.sav")
eln_reg = pickle.load(open(filename, 'rb'))
print(eln_reg)
ElasticNet(alpha=1.0, copy_X=True, fit_intercept=True, l1_ratio=0.5,
           max_iter=1000, normalize=False, positive=False, precompute=False,
           random_state=42, selection='cyclic', tol=0.0001, warm_start=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [94]:
#r = calcola_metriche(eln_reg, 'ElasticNet Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "eln_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "eln_regressor_res.csv"), index_col='nome')
r
Out[94]:
rmse mae r2 exp_var
nome
ElasticNet Regressor 8.22626392 6.01739502 0.60941488 0.60960162
In [95]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [96]:
modelli.append(eln_reg)
modelli_lineari.append(eln_reg)
nomi.append("ElasticNet Regressor")
nomi_modelli_lineari.append("ElasticNet Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [97]:
#params = {
#    'l1_ratio': [0, 0.2, 0.5, 0.7, 1],
#    'alpha': [25,10,4,2,1.0,0.8,0.5,0.3,0.2,0.1],
#    'positive' : [True, False],
#    'selection' : ['cyclic', 'random']
#}

#eln_grid_search = GridSearchCV(eln_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1)
#eln_grid_search.fit(X_train, y_train)
#best_eln = eln_grid_search.best_estimator_
#best_eln

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "eln_regressor_tuned.sav")
#pickle.dump(best_eln, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "eln_regressor_tuned.sav")
best_eln = pickle.load(open(filename, 'rb'))
print(best_eln)
ElasticNet(alpha=0.1, copy_X=True, fit_intercept=True, l1_ratio=0,
           max_iter=1000, normalize=False, positive=False, precompute=False,
           random_state=42, selection='cyclic', tol=0.0001, warm_start=False)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [98]:
#r = calcola_metriche(best_eln, 'ElasticNet Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "eln_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "eln_regressor_tuned_res.csv"), index_col='nome')
r
Out[98]:
rmse mae r2 exp_var
nome
ElasticNet Regressor Tuned 7.47664389 5.40263160 0.67729176 0.67747066
In [99]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [100]:
modelli.append(best_eln)
modelli_lineari.append(best_eln)
nomi.append("ElasticNet Regressor Tuned")
nomi_modelli_lineari.append("ElasticNet Regressor Tuned")

Least Angle Regression (Lars):

In [101]:
from sklearn.linear_model import Lars

#lars_reg = Lars()
#lars_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "lars_regressor.sav")
#pickle.dump(lars_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "lars_regressor.sav")
lars_reg = pickle.load(open(filename, 'rb'))
print(lars_reg)
Lars(copy_X=True, eps=2.220446049250313e-16, fit_intercept=True, fit_path=True,
     n_nonzero_coefs=500, normalize=True, precompute='auto', verbose=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [102]:
#r = calcola_metriche(lars_reg, 'Lars Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "lars_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "lars_regressor_res.csv"), index_col='nome')
r
Out[102]:
rmse mae r2 exp_var
nome
Lars Regressor 3007090620771253760.00000000 25199487040809752.00000000 -52239506507723541029891088379281408.00000000 -52202821461021237202405063930150912.00000000
In [103]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [104]:
modelli.append(lars_reg)
modelli_lineari.append(lars_reg)
nomi.append("Lars Regressor")
nomi_modelli_lineari.append("Lars Regressor")

Lasso Regressor:

In [105]:
from sklearn.linear_model import Lasso

#lasso_reg = Lasso()
#lasso_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "lasso_regressor.sav")
#pickle.dump(lasso_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "lasso_regressor.sav")
lasso_reg = pickle.load(open(filename, 'rb'))
print(lasso_reg)
Lasso(alpha=1.0, copy_X=True, fit_intercept=True, max_iter=1000,
      normalize=False, positive=False, precompute=False, random_state=None,
      selection='cyclic', tol=0.0001, warm_start=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [106]:
#r = calcola_metriche(lasso_reg, 'Lasso Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "lasso_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "lasso_regressor_res.csv"), index_col='nome')
r
Out[106]:
rmse mae r2 exp_var
nome
Lasso Regressor 7.77218663 5.69168146 0.65131377 0.65151734
In [107]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [108]:
modelli.append(lasso_reg)
modelli_lineari.append(lasso_reg)
nomi.append("Lasso Regressor")
nomi_modelli_lineari.append("Lasso Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [109]:
#params = {
#    'normalize': [True, False],
#    'alpha': [25,10,4,2,1.0,0.8,0.5,0.3,0.2,0],
#    'positive' : [True, False],
#    'precompute': ['auto', False],
#    'selection' : ['cyclic', 'random']
#}

#lasso_grid_search = GridSearchCV(lasso_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1)
#lasso_grid_search.fit(X_train, y_train)
#best_lasso = lasso_grid_search.best_estimator_
#best_lasso

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "lasso_regressor_tuned.sav")
#pickle.dump(best_lasso, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "lasso_regressor_tuned.sav")
best_lasso = pickle.load(open(filename, 'rb'))
print(best_lasso)
Lasso(alpha=0, copy_X=True, fit_intercept=True, max_iter=1000, normalize=True,
      positive=False, precompute=False, random_state=None, selection='cyclic',
      tol=0.0001, warm_start=False)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [110]:
#r = calcola_metriche(best_lasso, 'Lasso Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "lasso_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "lasso_regressor_tuned_res.csv"), index_col='nome')
r
Out[110]:
rmse mae r2 exp_var
nome
Lasso Regressor Tuned 7.40192819 5.37145254 0.68366261 0.68387587
In [111]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [112]:
modelli.append(best_lasso)
modelli_lineari.append(best_lasso)
nomi.append("Lasso Regressor Tuned")
nomi_modelli_lineari.append("Lasso Regressor Tuned")

Bayesian Regressor:

In [113]:
from sklearn.linear_model import BayesianRidge

#bayesian_reg = BayesianRidge()
#bayesian_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "bayesian_regressor.sav")
#pickle.dump(bayesian_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "bayesian_regressor.sav")
bayesian_reg = pickle.load(open(filename, 'rb'))
print(bayesian_reg)
BayesianRidge(alpha_1=1e-06, alpha_2=1e-06, alpha_init=None,
              compute_score=False, copy_X=True, fit_intercept=True,
              lambda_1=1e-06, lambda_2=1e-06, lambda_init=None, n_iter=300,
              normalize=False, tol=0.001, verbose=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [114]:
#r = calcola_metriche(bayesian_reg, 'Bayesian Ridge Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "bayesian_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "bayesian_regressor_res.csv"), index_col='nome')
r
Out[114]:
rmse mae r2 exp_var
nome
Bayesian Ridge Regressor 7.38950786 5.35896556 0.68472929 0.68492694
In [115]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [116]:
modelli.append(bayesian_reg)
modelli_lineari.append(bayesian_reg)
nomi.append("Bayesian Ridge Regressor")
nomi_modelli_lineari.append("Bayesian Ridge Regressor")

Modelli Instance Based

In [117]:
modelli_ib = []
nomi_modelli_ib = []

K Neighbors Regressor:

In [118]:
from sklearn.neighbors import KNeighborsRegressor

#knn_reg = KNeighborsRegressor()
#knn_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "knn_regressor.sav")
#pickle.dump(knn_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "knn_regressor.sav")
knn_reg = pickle.load(open(filename, 'rb'))
print(knn_reg)
KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
                    metric_params=None, n_jobs=None, n_neighbors=5, p=2,
                    weights='uniform')

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [119]:
#r = calcola_metriche(knn_reg, 'KNeighbors Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "knn_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "knn_regressor_res.csv"), index_col='nome')
r
Out[119]:
rmse mae r2 exp_var
nome
KNeighbors Regressor 11.18271259 8.12499862 0.27756747 0.27969190
In [120]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [121]:
modelli.append(knn_reg)
modelli_ib.append(knn_reg)
nomi.append("KNeighbors Regressor")
nomi_modelli_ib.append("KNeighbors Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [122]:
#params = {
#    'n_neighbors': [3,5,7,10,15,20,50],
#    'weights': ['uniform','distance'],
#    'p': [1,2],
#    'leaf_size': [20,30,50]
#}

#knn_grid_search = GridSearchCV(knn_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1)
#knn_grid_search.fit(X_train, y_train)
#best_knn = knn_grid_search.best_estimator_
#best_knn

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "knn_regressor_tuned.sav")
#pickle.dump(best_knn, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "knn_regressor_tuned.sav")
best_knn = pickle.load(open(filename, 'rb'))
print(best_knn)
KNeighborsRegressor(algorithm='auto', leaf_size=20, metric='minkowski',
                    metric_params=None, n_jobs=None, n_neighbors=5, p=1,
                    weights='distance')

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [123]:
#r = calcola_metriche(best_knn, 'KNeighbors Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "knn_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "knn_regressor_tuned_res.csv"), index_col='nome')
r
Out[123]:
rmse mae r2 exp_var
nome
KNeighbors Regressor Tuned 10.23343996 7.26734909 0.39503100 0.39891030
In [124]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [125]:
modelli.append(best_knn)
modelli_ib.append(best_knn)
nomi.append("KNeighbors Regressor Tuned")
nomi_modelli_ib.append("KNeighbors Regressor Tuned")

Radius Neighbors Regressor:

In [126]:
from sklearn.neighbors import RadiusNeighborsRegressor

#rn_reg = RadiusNeighborsRegressor()
#rn_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "rn_regressor.sav")
#pickle.dump(rn_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "rn_regressor.sav")
rn_reg = pickle.load(open(filename, 'rb'))
print(rn_reg)
RadiusNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
                         metric_params=None, n_jobs=None, p=2, radius=1.0,
                         weights='uniform')

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [127]:
#r = calcola_metriche(rn_reg, 'RadiusNeighbors Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "rn_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "rn_regressor_res.csv"), index_col='nome')
r
Out[127]:
rmse mae r2 exp_var
nome
RadiusNeighbors Regressor 9024265579062829056.00000000 8829457265309314048.00000000 -470475408932619499374830311889174528.00000000 -20061765893024328127722292256964608.00000000
In [128]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [129]:
modelli.append(rn_reg)
modelli_ib.append(rn_reg)
nomi.append("RadiusNeighbors Regressor")
nomi_modelli_ib.append("RadiusNeighbors Regressor")

Modelli SVM (Support Vector Machine)

In [130]:
modelli_svm = []
nomi_modelli_svm = []

Linear Support Vector Regression:

In [131]:
from sklearn.svm import LinearSVR

#lsvr_reg = LinearSVR(random_state=42)
#lsvr_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "lsvr_regressor.sav")
#pickle.dump(lsvr_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "lsvr_regressor.sav")
lsvr_reg = pickle.load(open(filename, 'rb'))
print(lsvr_reg)
LinearSVR(C=1.0, dual=True, epsilon=0.0, fit_intercept=True,
          intercept_scaling=1.0, loss='epsilon_insensitive', max_iter=1000,
          random_state=42, tol=0.0001, verbose=0)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [132]:
#r = calcola_metriche(lsvr_reg, 'LinearSVR Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "lsvr_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "lsvr_regressor_res.csv"), index_col='nome')
r
Out[132]:
rmse mae r2 exp_var
nome
LinearSVR Regressor 10.37847998 7.62172526 0.38156598 0.54851207
In [133]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [134]:
modelli.append(lsvr_reg)
modelli_svm.append(lsvr_reg)
nomi.append("LinearSVR Regressor")
nomi_modelli_svm.append("LinearSVR Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [135]:
#params = {
#    'loss': ['epsilon_insensitive', 'squared_epsilon_insensitive'],
#    'C': [0.1,0.5,1.0,1.5,2,3],
#    'tol': [0.0001,0.0002,0.001,0.002,0.01]
#}

#lsvr_grid_search = GridSearchCV(lsvr_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1)
#lsvr_grid_search.fit(X_train, y_train)
#best_lsvr = lsvr_grid_search.best_estimator_
#best_lsvr

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "lsvr_regressor_tuned.sav")
#pickle.dump(best_lsvr, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "lsvr_regressor_tuned.sav")
best_lsvr = pickle.load(open(filename, 'rb'))
print(best_lsvr)
LinearSVR(C=3, dual=True, epsilon=0.0, fit_intercept=True,
          intercept_scaling=1.0, loss='epsilon_insensitive', max_iter=1000,
          random_state=42, tol=0.0001, verbose=0)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [136]:
#r = calcola_metriche(best_lsvr, 'LinearSVR Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "lsvr_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "lsvr_regressor_tuned_res.csv"), index_col='nome')
r
Out[136]:
rmse mae r2 exp_var
nome
LinearSVR Regressor Tuned 10.65787918 8.14259624 0.34529435 0.50541299
In [137]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [138]:
modelli.append(best_lsvr)
modelli_svm.append(best_lsvr)
nomi.append("LinearSVR Regressor Tuned")
nomi_modelli_svm.append("LinearSVR Regressor Tuned")

Support Vector Regression:

In [139]:
from sklearn.svm import SVR

#svr_reg = SVR()
#svr_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "svr_regressor.sav")
#pickle.dump(svr_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "svr_regressor.sav")
svr_reg = pickle.load(open(filename, 'rb'))
print(svr_reg)
SVR(C=1.0, cache_size=200, coef0=0.0, degree=3, epsilon=0.1, gamma='scale',
    kernel='rbf', max_iter=-1, shrinking=True, tol=0.001, verbose=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [140]:
#r = calcola_metriche(svr_reg, 'SVR Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "svr_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "svr_regressor_res.csv"), index_col='nome')
r
Out[140]:
rmse mae r2 exp_var
nome
SVR Regressor 13.24212604 10.05793639 -0.01219368 0.01083540
In [141]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [142]:
modelli.append(svr_reg)
modelli_svm.append(svr_reg)
nomi.append("SVR Regressor")
nomi_modelli_svm.append("SVR Regressor")

Modelli basati su Reti Neurali Artificiali

In [143]:
modelli_ann = []
nomi_modelli_ann = []

Multi-layer Perceptron regressor:

In [144]:
from sklearn.neural_network import MLPRegressor

#mlpr_reg = MLPRegressor()
#mlpr_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "mlpr_regressor.sav")
#pickle.dump(mlpr_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "mlpr_regressor.sav")
mlpr_reg = pickle.load(open(filename, 'rb'))
print(mlpr_reg)
MLPRegressor(activation='relu', alpha=0.0001, batch_size='auto', beta_1=0.9,
             beta_2=0.999, early_stopping=False, epsilon=1e-08,
             hidden_layer_sizes=(100,), learning_rate='constant',
             learning_rate_init=0.001, max_fun=15000, max_iter=200,
             momentum=0.9, n_iter_no_change=10, nesterovs_momentum=True,
             power_t=0.5, random_state=None, shuffle=True, solver='adam',
             tol=0.0001, validation_fraction=0.1, verbose=False,
             warm_start=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [145]:
#r = calcola_metriche(mlpr_reg, 'Multi-layer Perceptron Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "mlpr_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "mlpr_regressor_res.csv"), index_col='nome')
r
Out[145]:
rmse mae r2 exp_var
nome
Multi-layer Perceptron Regressor 6.84910450 5.16811557 0.64803479 0.78947210
In [146]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [147]:
modelli.append(mlpr_reg)
modelli_ann.append(mlpr_reg)
nomi.append("Multi-layer Perceptron Regressor")
nomi_modelli_ann.append("Multi-layer Perceptron Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [148]:
#params = {
#    'activation': ['relu', 'tanh', 'logistic'],
#    'solver': ['adam', 'lbfgs'],
#    'hidden_layer_sizes': [10,25,50,100,150],
#    'learning_rate' : ['constant', 'adaptive'],
#    'max_iter' : [500]
#}

#mlpr_grid_search = GridSearchCV(mlpr_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1, verbose = 3)
#mlpr_grid_search.fit(X_train, y_train)
#best_mlpr = mlpr_grid_search.best_estimator_
#best_mlpr

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "mlpr_regressor_tuned.sav")
#pickle.dump(best_mlpr, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "mlpr_regressor_tuned.sav")
best_mlpr = pickle.load(open(filename, 'rb'))
print(best_mlpr)
MLPRegressor(activation='logistic', alpha=0.0001, batch_size='auto', beta_1=0.9,
             beta_2=0.999, early_stopping=False, epsilon=1e-08,
             hidden_layer_sizes=150, learning_rate='constant',
             learning_rate_init=0.001, max_fun=15000, max_iter=500,
             momentum=0.9, n_iter_no_change=10, nesterovs_momentum=True,
             power_t=0.5, random_state=None, shuffle=True, solver='adam',
             tol=0.0001, validation_fraction=0.1, verbose=False,
             warm_start=False)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [149]:
#r = calcola_metriche(best_mlpr, 'Multi-layer Perceptron Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "mlpr_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "mlpr_regressor_tuned_res.csv"), index_col='nome')
r
Out[149]:
rmse mae r2 exp_var
nome
Multi-layer Perceptron Regressor Tuned 3.85632390 2.78465478 0.91532705 0.91531888
In [150]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [151]:
modelli.append(best_mlpr)
modelli_ann.append(best_mlpr)
nomi.append("Multi-layer Perceptron Regressor Tuned")
nomi_modelli_ann.append("Multi-layer Perceptron Regressor Tuned")

Modelli basati si Alberi

In [152]:
modelli_tree = []
nomi_modelli_tree = []

Decision Tree Regressor:

In [153]:
from sklearn.tree import DecisionTreeRegressor

#dtree_reg = DecisionTreeRegressor(random_state=42)
#dtree_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "dtree_regressor.sav")
#pickle.dump(dtree_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "dtree_regressor.sav")
dtree_reg = pickle.load(open(filename, 'rb'))
print(dtree_reg)
DecisionTreeRegressor(ccp_alpha=0.0, criterion='mse', max_depth=None,
                      max_features=None, max_leaf_nodes=None,
                      min_impurity_decrease=0.0, min_impurity_split=None,
                      min_samples_leaf=1, min_samples_split=2,
                      min_weight_fraction_leaf=0.0, presort='deprecated',
                      random_state=42, splitter='best')

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [154]:
#r = calcola_metriche(dtree_reg, 'Decision Tree Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "dtree_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "dtree_regressor_res.csv"), index_col='nome')
r
Out[154]:
rmse mae r2 exp_var
nome
Decision Tree Regressor 4.04448838 2.96178315 0.90562246 0.90578328
In [155]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [156]:
modelli.append(dtree_reg)
modelli_tree.append(dtree_reg)
nomi.append("Decision Tree Regressor")
nomi_modelli_tree.append("Decision Tree Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [157]:
#params = {
#    'criterion': ['mse', 'friedman_mse', 'mae'],
#    'splitter': ['best', 'random'],
#    'max_features' : ['auto', 'sqrt', None]
#}

#dtree_grid_search = GridSearchCV(dtree_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1, verbose = 3)
#dtree_grid_search.fit(X_train, y_train)
#best_dtree = dtree_grid_search.best_estimator_
#best_dtree

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "dtree_regressor_tuned.sav")
#pickle.dump(best_dtree, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "dtree_regressor_tuned.sav")
best_dtree = pickle.load(open(filename, 'rb'))
print(best_dtree)
DecisionTreeRegressor(ccp_alpha=0.0, criterion='friedman_mse', max_depth=None,
                      max_features='auto', max_leaf_nodes=None,
                      min_impurity_decrease=0.0, min_impurity_split=None,
                      min_samples_leaf=1, min_samples_split=2,
                      min_weight_fraction_leaf=0.0, presort='deprecated',
                      random_state=42, splitter='best')

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [158]:
#r = calcola_metriche(best_dtree, 'Decision Tree Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "dtree_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "dtree_regressor_tuned_res.csv"), index_col='nome')
r
Out[158]:
rmse mae r2 exp_var
nome
Decision Tree Regressor Tuned 4.02842329 2.94809076 0.90637045 0.90653447
In [159]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [160]:
modelli.append(best_dtree)
modelli_tree.append(best_dtree)
nomi.append("Decision Tree Regressor Tuned")
nomi_modelli_tree.append("Decision Tree Regressor Tuned")

Extra Tree Regressor:

In [161]:
from sklearn.tree import ExtraTreeRegressor

#etree_reg = ExtraTreeRegressor(random_state=42)
#etree_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "etree_regressor.sav")
#pickle.dump(etree_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "etree_regressor.sav")
etree_reg = pickle.load(open(filename, 'rb'))
print(etree_reg)
ExtraTreeRegressor(ccp_alpha=0.0, criterion='mse', max_depth=None,
                   max_features='auto', max_leaf_nodes=None,
                   min_impurity_decrease=0.0, min_impurity_split=None,
                   min_samples_leaf=1, min_samples_split=2,
                   min_weight_fraction_leaf=0.0, random_state=42,
                   splitter='random')

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [162]:
#r = calcola_metriche(etree_reg, 'Extra Tree Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "etree_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "etree_regressor_res.csv"), index_col='nome')
r
Out[162]:
rmse mae r2 exp_var
nome
Extra Tree Regressor 4.16123869 3.03273049 0.90008647 0.90016340
In [163]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [164]:
modelli.append(etree_reg)
modelli_tree.append(etree_reg)
nomi.append("Extra Tree Regressor")
nomi_modelli_tree.append("Extra Tree Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [165]:
#params = {
#    'criterion': ['mse', 'friedman_mse', 'mae'],
#    'splitter': ['best', 'random'],
#    'max_features' : ['auto', 'sqrt', None, 'log2',1]
#}

#etree_grid_search = GridSearchCV(etree_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1, verbose = 3)
#etree_grid_search.fit(X_train, y_train)
#best_etree = etree_grid_search.best_estimator_
#best_etree

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "etree_regressor_tuned.sav")
#pickle.dump(best_etree, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "etree_regressor_tuned.sav")
best_etree = pickle.load(open(filename, 'rb'))
print(best_etree)
ExtraTreeRegressor(ccp_alpha=0.0, criterion='friedman_mse', max_depth=None,
                   max_features='auto', max_leaf_nodes=None,
                   min_impurity_decrease=0.0, min_impurity_split=None,
                   min_samples_leaf=1, min_samples_split=2,
                   min_weight_fraction_leaf=0.0, random_state=42,
                   splitter='best')

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [166]:
#r = calcola_metriche(best_etree, 'Extra Tree Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "etree_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "etree_regressor_tuned_res.csv"), index_col='nome')
r
Out[166]:
rmse mae r2 exp_var
nome
Extra Tree Regressor Tuned 4.02842329 2.94809076 0.90637045 0.90653447
In [167]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [168]:
modelli.append(best_etree)
modelli_tree.append(best_etree)
nomi.append("Extra Tree Regressor Tuned")
nomi_modelli_tree.append("Extra Tree Regressor Tuned")

Modelli di Ensemble Learning

In [169]:
modelli_ensemble = []
nomi_modelli_ensemble = []

Random Forest Regressor:

In [170]:
from sklearn.ensemble import RandomForestRegressor

#rndf_reg = RandomForestRegressor(random_state=42)
#rndf_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "rndf_regressor.sav")
#pickle.dump(rndf_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "rndf_regressor.sav")
rndf_reg = pickle.load(open(filename, 'rb'))
print(rndf_reg)
RandomForestRegressor(bootstrap=True, ccp_alpha=0.0, criterion='mse',
                      max_depth=None, max_features='auto', max_leaf_nodes=None,
                      max_samples=None, min_impurity_decrease=0.0,
                      min_impurity_split=None, min_samples_leaf=1,
                      min_samples_split=2, min_weight_fraction_leaf=0.0,
                      n_estimators=100, n_jobs=None, oob_score=False,
                      random_state=42, verbose=0, warm_start=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [171]:
#r = calcola_metriche(rndf_reg, 'Random Forest Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "rndf_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "rndf_regressor_res.csv"), index_col='nome')
r
Out[171]:
rmse mae r2 exp_var
nome
Random Forest Regressor 2.88782659 2.12843124 0.95187763 0.95194946
In [172]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [173]:
modelli.append(rndf_reg)
modelli_ensemble.append(rndf_reg)
nomi.append("Random Forest Regressor")
nomi_modelli_ensemble.append("Random Forest Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [174]:
#params = {
#    'n_estimators' : [10,50,70,100,150],
#    'criterion': ['mse', 'mae'],
#    'max_features' : ['auto', 'sqrt','log2', None],
#    'bootstrap' : [True, False],
#    'n_jobs' : [-1]
#}

#rndfr_grid_search = GridSearchCV(rndf_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1, verbose = 3)
#rndfr_grid_search.fit(X_train, y_train)
#best_rndfr = rndfr_grid_search.best_estimator_
#best_rndfr

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "rndf_regressor_tuned.sav")
#pickle.dump(best_rndfr, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "rndf_regressor_tuned.sav")
best_rndfr = pickle.load(open(filename, 'rb'))
print(best_rndfr)
RandomForestRegressor(bootstrap=True, ccp_alpha=0.0, criterion='mae',
                      max_depth=None, max_features='auto', max_leaf_nodes=None,
                      max_samples=None, min_impurity_decrease=0.0,
                      min_impurity_split=None, min_samples_leaf=1,
                      min_samples_split=2, min_weight_fraction_leaf=0.0,
                      n_estimators=150, n_jobs=-1, oob_score=False,
                      random_state=42, verbose=0, warm_start=False)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [175]:
#r = calcola_metriche(best_rndfr, 'Random Forest Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "rndf_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "rndf_regressor_tuned_res.csv"), index_col='nome')
r
Out[175]:
rmse mae r2 exp_var
nome
Random Forest Regressor Tuned 2.86870254 2.11638826 0.95251194 0.95258488
In [176]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [177]:
modelli.append(best_rndfr)
modelli_ensemble.append(best_rndfr)
nomi.append("Random Forest Regressor Tuned")
nomi_modelli_ensemble.append("Random Forest Regressor Tuned")

Bagging Regressor:

In [178]:
from sklearn.ensemble import BaggingRegressor

#bagging_reg = BaggingRegressor(random_state=42,n_jobs=-1)
#bagging_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "bagging_regressor.sav")
#pickle.dump(bagging_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "bagging_regressor.sav")
bagging_reg = pickle.load(open(filename, 'rb'))
print(bagging_reg)
BaggingRegressor(base_estimator=None, bootstrap=True, bootstrap_features=False,
                 max_features=1.0, max_samples=1.0, n_estimators=10, n_jobs=-1,
                 oob_score=False, random_state=42, verbose=0, warm_start=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [179]:
#r = calcola_metriche(bagging_reg, 'Bagging Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "bagging_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "bagging_regressor_res.csv"), index_col='nome')
r
Out[179]:
rmse mae r2 exp_var
nome
Bagging Regressor 3.02735003 2.23429724 0.94712758 0.94720019
In [180]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [181]:
modelli.append(bagging_reg)
modelli_ensemble.append(bagging_reg)
nomi.append("Bagging Regressor")
nomi_modelli_ensemble.append("Bagging Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [182]:
#params = {
#    'n_estimators' : [10,50,70,100,150],
#    'bootstrap_features' : [True, False],
#    'bootstrap' : [True, False],
#    'max_features' : [1.0,5,10]
#}

#bagging_grid_search = GridSearchCV(bagging_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1, verbose = 3)
#bagging_grid_search.fit(X_train, y_train)
#best_bagging = bagging_grid_search.best_estimator_
#best_bagging

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "bagging_regressor_tuned.sav")
#pickle.dump(best_bagging, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "bagging_regressor_tuned.sav")
best_bagging = pickle.load(open(filename, 'rb'))
print(best_bagging)
BaggingRegressor(base_estimator=None, bootstrap=True, bootstrap_features=False,
                 max_features=1.0, max_samples=1.0, n_estimators=150, n_jobs=-1,
                 oob_score=False, random_state=42, verbose=0, warm_start=False)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [183]:
#r = calcola_metriche(best_bagging, 'Bagging Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "bagging_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "bagging_regressor_tuned_res.csv"), index_col='nome')
r
Out[183]:
rmse mae r2 exp_var
nome
Bagging Regressor Tuned 2.88098134 2.12588067 0.95210669 0.95218108
In [184]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [185]:
modelli.append(best_bagging)
modelli_ensemble.append(best_bagging)
nomi.append("Bagging Regressor Tuned")
nomi_modelli_ensemble.append("Bagging Regressor Tuned")

AdaBoost Regressor:

In [186]:
from sklearn.ensemble import AdaBoostRegressor

#adab_reg = AdaBoostRegressor(base_estimator=DecisionTreeRegressor(),random_state=42)
#adab_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "adab_regressor.sav")
#pickle.dump(adab_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "adab_regressor.sav")
adab_reg = pickle.load(open(filename, 'rb'))
print(adab_reg)
AdaBoostRegressor(base_estimator=DecisionTreeRegressor(ccp_alpha=0.0,
                                                       criterion='mse',
                                                       max_depth=None,
                                                       max_features=None,
                                                       max_leaf_nodes=None,
                                                       min_impurity_decrease=0.0,
                                                       min_impurity_split=None,
                                                       min_samples_leaf=1,
                                                       min_samples_split=2,
                                                       min_weight_fraction_leaf=0.0,
                                                       presort='deprecated',
                                                       random_state=None,
                                                       splitter='best'),
                  learning_rate=1.0, loss='linear', n_estimators=50,
                  random_state=42)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [187]:
#r = calcola_metriche(adab_reg, 'AdaBoost Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "adab_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "adab_regressor_res.csv"), index_col='nome')
r
Out[187]:
rmse mae r2 exp_var
nome
AdaBoost Regressor 2.99686628 2.17989530 0.94817182 0.94826184
In [188]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [189]:
modelli.append(adab_reg)
modelli_ensemble.append(adab_reg)
nomi.append("AdaBoost Regressor")
nomi_modelli_ensemble.append("AdaBoost Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [190]:
#params = {
#    'n_estimators' : [10,50,70,100,150],
#    'loss' : ['linear', 'square', 'exponential'],
#    'learning_rate' : [1.0,0.5,0.7]
#}

#adab_grid_search = GridSearchCV(adab_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1, verbose = 3)
#adab_grid_search.fit(X_train, y_train)
#best_adab = adab_grid_search.best_estimator_
#best_adab

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "adab_regressor_tuned.sav")
#pickle.dump(best_adab, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "adab_regressor_tuned.sav")
best_adab = pickle.load(open(filename, 'rb'))
print(best_adab)
AdaBoostRegressor(base_estimator=DecisionTreeRegressor(ccp_alpha=0.0,
                                                       criterion='mse',
                                                       max_depth=None,
                                                       max_features=None,
                                                       max_leaf_nodes=None,
                                                       min_impurity_decrease=0.0,
                                                       min_impurity_split=None,
                                                       min_samples_leaf=1,
                                                       min_samples_split=2,
                                                       min_weight_fraction_leaf=0.0,
                                                       presort='deprecated',
                                                       random_state=None,
                                                       splitter='best'),
                  learning_rate=1.0, loss='linear', n_estimators=150,
                  random_state=42)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [191]:
#r = calcola_metriche(best_adab, 'AdaBoost Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "adab_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "adab_regressor_tuned_res.csv"), index_col='nome')
r
Out[191]:
rmse mae r2 exp_var
nome
AdaBoost Regressor Tuned 2.96497301 2.15074503 0.94926862 0.94934583
In [192]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [193]:
modelli.append(best_adab)
modelli_ensemble.append(best_adab)
nomi.append("AdaBoost Regressor Tuned")
nomi_modelli_ensemble.append("AdaBoost Regressor Tuned")

Gradient Boosting Regressor:

In [194]:
from sklearn.ensemble import GradientBoostingRegressor

#gb_reg = GradientBoostingRegressor(random_state=42)
#gb_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "gb_regressor.sav")
#pickle.dump(gb_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "gb_regressor.sav")
gb_reg = pickle.load(open(filename, 'rb'))
print(gb_reg)
GradientBoostingRegressor(alpha=0.9, ccp_alpha=0.0, criterion='friedman_mse',
                          init=None, learning_rate=0.1, loss='ls', max_depth=3,
                          max_features=None, max_leaf_nodes=None,
                          min_impurity_decrease=0.0, min_impurity_split=None,
                          min_samples_leaf=1, min_samples_split=2,
                          min_weight_fraction_leaf=0.0, n_estimators=100,
                          n_iter_no_change=None, presort='deprecated',
                          random_state=42, subsample=1.0, tol=0.0001,
                          validation_fraction=0.1, verbose=0, warm_start=False)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [195]:
#r = calcola_metriche(gb_reg, 'Gradient Boosting Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "gb_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "gb_regressor_res.csv"), index_col='nome')
r
Out[195]:
rmse mae r2 exp_var
nome
Gradient Boosting Regressor 2.97590024 2.18927910 0.94889345 0.94897208
In [196]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [197]:
modelli.append(gb_reg)
modelli_ensemble.append(gb_reg)
nomi.append("Gradient Boosting Regressor")
nomi_modelli_ensemble.append("Gradient Boosting Regressor")

Procedo ad effettuare parameter tuning per tale modello mediante l'utilizzo di Grid Search:

In [198]:
#params = {
#    'n_estimators' : [10,50,100,150],
#    'loss' : ['ls', 'lad', 'huber'],
#    'learning_rate' : [1.0,0.5,0.7,0.1],
#    'max_depth' : [3,5,10]
#}

#gb_grid_search = GridSearchCV(gb_reg, params, cv = 5, scoring = 'neg_mean_squared_error', n_jobs = -1, verbose = 3)
#gb_grid_search.fit(X_train, y_train)
#best_gb = gb_grid_search.best_estimator_
#best_gb

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "gb_regressor_tuned.sav")
#pickle.dump(best_gb, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "gb_regressor_tuned.sav")
best_gb = pickle.load(open(filename, 'rb'))
print(best_gb)
GradientBoostingRegressor(alpha=0.9, ccp_alpha=0.0, criterion='friedman_mse',
                          init=None, learning_rate=0.1, loss='ls', max_depth=5,
                          max_features=None, max_leaf_nodes=None,
                          min_impurity_decrease=0.0, min_impurity_split=None,
                          min_samples_leaf=1, min_samples_split=2,
                          min_weight_fraction_leaf=0.0, n_estimators=100,
                          n_iter_no_change=None, presort='deprecated',
                          random_state=42, subsample=1.0, tol=0.0001,
                          validation_fraction=0.1, verbose=0, warm_start=False)

Si procede a verificare le prestazioni del modello tuned tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [199]:
#r = calcola_metriche(best_gb, 'Gradient Boosting Regressor Tuned', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "gb_regressor_tuned_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "gb_regressor_tuned_res.csv"), index_col='nome')
r
Out[199]:
rmse mae r2 exp_var
nome
Gradient Boosting Regressor Tuned 2.80942929 2.07125914 0.95445954 0.95452063
In [200]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [201]:
modelli.append(best_gb)
modelli_ensemble.append(best_gb)
nomi.append("Gradient Boosting Regressor Tuned")
nomi_modelli_ensemble.append("Gradient Boosting Regressor Tuned")

Stacked Generalization:

Si procede nell'applicazione della tecnica definita Stacked Generalization tramite l'impiego dell'estimator StackingRegressor della libreria sklearn.ensemble.

In [202]:
from sklearn.ensemble import StackingRegressor

#definisco gli estimators che faranno parte del primo livello:
#estimators_list = [
#    ('decision_tree', DecisionTreeRegressor(criterion='friedman_mse', max_features='auto', random_state=42)),
    #('extra_tree', ExtraTreeRegressor(criterion='friedman_mse', splitter='best', random_state=42)),
#    ('random_f',RandomForestRegressor(n_estimators=150,random_state=42))
#]

#definisco il final_estimator, ossia il blender che metterà assieme i risultati dei precedenti estimator, fornendo il risultato finale
#blender = GradientBoostingRegressor(loss='ls',max_depth=5,random_state=42)

#stacking_reg = StackingRegressor(estimators=estimators_list, final_estimator=blender, n_jobs=-1, passthrough=True)
#stacking_reg.fit(X_train, y_train)

#salvo il modello su disco
#filename = os.path.join(MODELS_PATH, "stacking_regressor.sav")
#pickle.dump(stacking_reg, open(filename, 'wb'))

#carico il modello dal disco
filename = os.path.join(MODELS_PATH, "stacking_regressor.sav")
stacking_reg = pickle.load(open(filename, 'rb'))
print(stacking_reg)
StackingRegressor(cv=None,
                  estimators=[('decision_tree',
                               DecisionTreeRegressor(ccp_alpha=0.0,
                                                     criterion='friedman_mse',
                                                     max_depth=None,
                                                     max_features='auto',
                                                     max_leaf_nodes=None,
                                                     min_impurity_decrease=0.0,
                                                     min_impurity_split=None,
                                                     min_samples_leaf=1,
                                                     min_samples_split=2,
                                                     min_weight_fraction_leaf=0.0,
                                                     presort='deprecated',
                                                     random_state=42,
                                                     splitter='best')),
                              ('rand...
                                                            max_depth=5,
                                                            max_features=None,
                                                            max_leaf_nodes=None,
                                                            min_impurity_decrease=0.0,
                                                            min_impurity_split=None,
                                                            min_samples_leaf=1,
                                                            min_samples_split=2,
                                                            min_weight_fraction_leaf=0.0,
                                                            n_estimators=100,
                                                            n_iter_no_change=None,
                                                            presort='deprecated',
                                                            random_state=42,
                                                            subsample=1.0,
                                                            tol=0.0001,
                                                            validation_fraction=0.1,
                                                            verbose=0,
                                                            warm_start=False),
                  n_jobs=-1, passthrough=True, verbose=0)

Si procede a verificare le prestazioni di tale modello tramite l'impiego del metodo calcola_metriche precedentemente definito:

In [203]:
#r = calcola_metriche(stacking_reg, 'Stacking Regressor', X_train, y_train)
#r.to_csv(os.path.join(RESULTS_PATH, "stacking_regressor_res.csv"), index = False)

r = ps.read_csv(os.path.join(RESULTS_PATH, "stacking_regressor_res.csv"), index_col='nome')
r
Out[203]:
rmse mae r2 exp_var
nome
Stacking Regressor 2.88016465 2.12127464 0.95213767 0.95220361
In [204]:
#Carico i risultati nel dataset che mi conterrà i risultati di tutti i modelli impiegati

risultati = risultati.append(r)
risultati.to_csv(os.path.join(RESULTS_PATH, "risultati.csv"))
risultati = ps.read_csv(os.path.join(RESULTS_PATH, "risultati.csv"), index_col='nome')
In [205]:
modelli.append(stacking_reg)
modelli_ensemble.append(stacking_reg)
nomi.append("Stacking Regressor")
nomi_modelli_ensemble.append("Stacking Regressor")

VALUTAZIONE DEI MODELLI SUL TRAINING SET

Come primo passo nella nostra analisi delle prestazioni dei modelli precedentemente addestrati sul training set, andiamo a mostrare i risultati ottenuti che sono stati caricati in corso d'opera appositamente in un DataFrame. Si ricorda inoltre che le metriche impiegate per la valutazione sono: root mean squared error, mean absolute error ( per entrambe valori inferiori suggeriscono un ridotto errore del modello), r2 ed explained variance (queste invece ammetteno valore massimo 1 e valori tendenti ad 1 suggeriscono la correttezza delle previsioni del modello, possono inoltre assumere valore negativo ed in questo caso avremo a che fare con modelli non corretti).

In [206]:
risultati
Out[206]:
rmse mae r2 exp_var
nome
Linear Regressor 7.40186339 5.37135068 0.68366814 0.68388143
Ridge Regressor 7.39787930 5.36856111 0.68400897 0.68421854
Ridge Regressor Tuned 7.38926769 5.35861469 0.68474985 0.68494719
SGD Regressor 698634553618519.62500000 495593827541853.87500000 -2864227922721051520127729664.00000000 -907476207591652740059627520.00000000
SGD Regressor Tuned 141.97593276 99.75022096 -114.81395536 -28.13841491
ElasticNet Regressor 8.22626392 6.01739502 0.60941488 0.60960162
ElasticNet Regressor Tuned 7.47664389 5.40263160 0.67729176 0.67747066
Lars Regressor 3007090620771253760.00000000 25199487040809752.00000000 -52239506507723541029891088379281408.00000000 -52202821461021237202405063930150912.00000000
Lasso Regressor 7.77218663 5.69168146 0.65131377 0.65151734
Lasso Regressor Tuned 7.40192819 5.37145254 0.68366261 0.68387587
Bayesian Ridge Regressor 7.38950786 5.35896556 0.68472929 0.68492694
KNeighbors Regressor 11.18271259 8.12499862 0.27756747 0.27969190
KNeighbors Regressor Tuned 10.23343996 7.26734909 0.39503100 0.39891030
RadiusNeighbors Regressor 9024265579062829056.00000000 8829457265309314048.00000000 -470475408932619499374830311889174528.00000000 -20061765893024328127722292256964608.00000000
LinearSVR Regressor 10.37847998 7.62172526 0.38156598 0.54851207
LinearSVR Regressor Tuned 10.65787918 8.14259624 0.34529435 0.50541299
SVR Regressor 13.24212604 10.05793639 -0.01219368 0.01083540
Multi-layer Perceptron Regressor 6.84910450 5.16811557 0.64803479 0.78947210
Multi-layer Perceptron Regressor Tuned 3.85632390 2.78465478 0.91532705 0.91531888
Decision Tree Regressor 4.04448838 2.96178315 0.90562246 0.90578328
Decision Tree Regressor Tuned 4.02842329 2.94809076 0.90637045 0.90653447
Extra Tree Regressor 4.16123869 3.03273049 0.90008647 0.90016340
Extra Tree Regressor Tuned 4.02842329 2.94809076 0.90637045 0.90653447
Random Forest Regressor 2.88782659 2.12843124 0.95187763 0.95194946
Random Forest Regressor Tuned 2.86870254 2.11638826 0.95251194 0.95258488
Bagging Regressor 3.02735003 2.23429724 0.94712758 0.94720019
Bagging Regressor Tuned 2.88098134 2.12588067 0.95210669 0.95218108
AdaBoost Regressor 2.99686628 2.17989530 0.94817182 0.94826184
AdaBoost Regressor Tuned 2.96497301 2.15074503 0.94926862 0.94934583
Gradient Boosting Regressor 2.97590024 2.18927910 0.94889345 0.94897208
Gradient Boosting Regressor Tuned 2.80942929 2.07125914 0.95445954 0.95452063
Stacking Regressor 2.88016465 2.12127464 0.95213767 0.95220361

Una prima visione del DataFrame contenente i risultati ci porta a scoprire la piena scorrettezza dei regressori SGD Regressor (anche in versione tuned), Lars Regressor e Radius Neighbors Regressor che quindi costituiscono i peggiori modelli per prestazioni misurate sul training set e probabilmente suggeriscono la presenza di underfiting, dati i valori delle metriche di errore così elevati e addirittura la presenza di valori negativi nettamente sotto lo 0 per le metriche r2 ed explained variance. Si procede perciò alla rimozione delle rispettive entry nel dataframe per una più corretta analisi nel proseguo.

In [207]:
bad_estimators = ["SGD Regressor", "SGD Regressor Tuned", "Lars Regressor", "RadiusNeighbors Regressor"]
risultati = risultati.drop(bad_estimators)

#procedo alla rimozione di tali modelli anche dalle liste in cui sono stati conservati nel corso della sezione precedente
modelli.remove(sgd_reg)
modelli.remove(best_sgd)
modelli.remove(lars_reg)
modelli_lineari.remove(sgd_reg)
modelli_lineari.remove(best_sgd)
modelli_lineari.remove(lars_reg)
nomi.remove("SGD Regressor")
nomi.remove("SGD Regressor Tuned")
nomi.remove("Lars Regressor")
nomi_modelli_lineari.remove("SGD Regressor")
nomi_modelli_lineari.remove("SGD Regressor Tuned")
nomi_modelli_lineari.remove("Lars Regressor")

modelli.remove(rn_reg)
modelli_ib.remove(rn_reg)
nomi.remove("RadiusNeighbors Regressor")
nomi_modelli_ib.remove("RadiusNeighbors Regressor")
In [208]:
risultati
Out[208]:
rmse mae r2 exp_var
nome
Linear Regressor 7.40186339 5.37135068 0.68366814 0.68388143
Ridge Regressor 7.39787930 5.36856111 0.68400897 0.68421854
Ridge Regressor Tuned 7.38926769 5.35861469 0.68474985 0.68494719
ElasticNet Regressor 8.22626392 6.01739502 0.60941488 0.60960162
ElasticNet Regressor Tuned 7.47664389 5.40263160 0.67729176 0.67747066
Lasso Regressor 7.77218663 5.69168146 0.65131377 0.65151734
Lasso Regressor Tuned 7.40192819 5.37145254 0.68366261 0.68387587
Bayesian Ridge Regressor 7.38950786 5.35896556 0.68472929 0.68492694
KNeighbors Regressor 11.18271259 8.12499862 0.27756747 0.27969190
KNeighbors Regressor Tuned 10.23343996 7.26734909 0.39503100 0.39891030
LinearSVR Regressor 10.37847998 7.62172526 0.38156598 0.54851207
LinearSVR Regressor Tuned 10.65787918 8.14259624 0.34529435 0.50541299
SVR Regressor 13.24212604 10.05793639 -0.01219368 0.01083540
Multi-layer Perceptron Regressor 6.84910450 5.16811557 0.64803479 0.78947210
Multi-layer Perceptron Regressor Tuned 3.85632390 2.78465478 0.91532705 0.91531888
Decision Tree Regressor 4.04448838 2.96178315 0.90562246 0.90578328
Decision Tree Regressor Tuned 4.02842329 2.94809076 0.90637045 0.90653447
Extra Tree Regressor 4.16123869 3.03273049 0.90008647 0.90016340
Extra Tree Regressor Tuned 4.02842329 2.94809076 0.90637045 0.90653447
Random Forest Regressor 2.88782659 2.12843124 0.95187763 0.95194946
Random Forest Regressor Tuned 2.86870254 2.11638826 0.95251194 0.95258488
Bagging Regressor 3.02735003 2.23429724 0.94712758 0.94720019
Bagging Regressor Tuned 2.88098134 2.12588067 0.95210669 0.95218108
AdaBoost Regressor 2.99686628 2.17989530 0.94817182 0.94826184
AdaBoost Regressor Tuned 2.96497301 2.15074503 0.94926862 0.94934583
Gradient Boosting Regressor 2.97590024 2.18927910 0.94889345 0.94897208
Gradient Boosting Regressor Tuned 2.80942929 2.07125914 0.95445954 0.95452063
Stacking Regressor 2.88016465 2.12127464 0.95213767 0.95220361

Prima di procedere alla visualizzazione ed analisi dei risultati andiamo a mantenere, ove possibile, unicamente le righe che fanno riferimento a modelli per cui è stato effettuato parameter tuning, perchè rispetto ai modelli di default hanno prestazioni migliori ( ad eccezione del Linear SVR Regressor per cui si notano risultati migliori nella forma di base).

In [209]:
#Rimozione Ridge Regressor
modelli.remove(ridge)
modelli_lineari.remove(ridge)
nomi.remove("Ridge Regressor")
nomi_modelli_lineari.remove("Ridge Regressor")

#Rimozione ElasticNet Regressor
modelli.remove(eln_reg)
modelli_lineari.remove(eln_reg)
nomi.remove("ElasticNet Regressor")
nomi_modelli_lineari.remove("ElasticNet Regressor")

#Rimozione Lasso Regressor
modelli.remove(lasso_reg)
modelli_lineari.remove(lasso_reg)
nomi.remove("Lasso Regressor")
nomi_modelli_lineari.remove("Lasso Regressor")

#Rimozione KNeighbors Regressor
modelli.remove(knn_reg)
modelli_ib.remove(knn_reg)
nomi.remove("KNeighbors Regressor")
nomi_modelli_ib.remove("KNeighbors Regressor")

#Rimozione LinearSVR Regressor Tuned
modelli.remove(best_lsvr)
modelli_svm.remove(best_lsvr)
nomi.remove("LinearSVR Regressor Tuned")
nomi_modelli_svm.remove("LinearSVR Regressor Tuned")

#Rimozione Multi-layer Perceptron Regressor
modelli.remove(mlpr_reg)
modelli_ann.remove(mlpr_reg)
nomi.remove("Multi-layer Perceptron Regressor")
nomi_modelli_ann.remove("Multi-layer Perceptron Regressor")

#Rimozione Decision Tree Regressor
modelli.remove(dtree_reg)
modelli_tree.remove(dtree_reg)
nomi.remove("Decision Tree Regressor")
nomi_modelli_tree.remove("Decision Tree Regressor")

#Rimozione Extra Tree Regressor
modelli.remove(etree_reg)
modelli_tree.remove(etree_reg)
nomi.remove("Extra Tree Regressor")
nomi_modelli_tree.remove("Extra Tree Regressor")

#Rimozione Random Forest Regressor
modelli.remove(rndf_reg)
modelli_ensemble.remove(rndf_reg)
nomi.remove("Random Forest Regressor")
nomi_modelli_ensemble.remove("Random Forest Regressor")

#Rimozione Bagging Regressor
modelli.remove(bagging_reg)
modelli_ensemble.remove(bagging_reg)
nomi.remove("Bagging Regressor")
nomi_modelli_ensemble.remove("Bagging Regressor")

#Rimozione AdaBoost Regressor
modelli.remove(adab_reg)
modelli_ensemble.remove(adab_reg)
nomi.remove("AdaBoost Regressor")
nomi_modelli_ensemble.remove("AdaBoost Regressor")

#Rimozione Gradient Boosting Regressor
modelli.remove(gb_reg)
modelli_ensemble.remove(gb_reg)
nomi.remove("Gradient Boosting Regressor")
nomi_modelli_ensemble.remove("Gradient Boosting Regressor")
In [210]:
#seleziono i risultati da mostrare una volta terminata la rimozione di cui sopra
risultati=risultati.loc[nomi_modelli_lineari+nomi_modelli_ib+nomi_modelli_svm+nomi_modelli_ann+nomi_modelli_tree+nomi_modelli_ensemble]
risultati
Out[210]:
rmse mae r2 exp_var
nome
Linear Regressor 7.40186339 5.37135068 0.68366814 0.68388143
Ridge Regressor Tuned 7.38926769 5.35861469 0.68474985 0.68494719
ElasticNet Regressor Tuned 7.47664389 5.40263160 0.67729176 0.67747066
Lasso Regressor Tuned 7.40192819 5.37145254 0.68366261 0.68387587
Bayesian Ridge Regressor 7.38950786 5.35896556 0.68472929 0.68492694
KNeighbors Regressor Tuned 10.23343996 7.26734909 0.39503100 0.39891030
LinearSVR Regressor 10.37847998 7.62172526 0.38156598 0.54851207
SVR Regressor 13.24212604 10.05793639 -0.01219368 0.01083540
Multi-layer Perceptron Regressor Tuned 3.85632390 2.78465478 0.91532705 0.91531888
Decision Tree Regressor Tuned 4.02842329 2.94809076 0.90637045 0.90653447
Extra Tree Regressor Tuned 4.02842329 2.94809076 0.90637045 0.90653447
Random Forest Regressor Tuned 2.86870254 2.11638826 0.95251194 0.95258488
Bagging Regressor Tuned 2.88098134 2.12588067 0.95210669 0.95218108
AdaBoost Regressor Tuned 2.96497301 2.15074503 0.94926862 0.94934583
Gradient Boosting Regressor Tuned 2.80942929 2.07125914 0.95445954 0.95452063
Stacking Regressor 2.88016465 2.12127464 0.95213767 0.95220361

Andiamo ora a visualizzare i valori delle rispettive metriche per ogni modello scelto da considerare:

In [211]:
plt.figure(figsize=(15,10))
sorted_rmse = risultati.sort_values(by = 'rmse')
sns.barplot(x = sorted_rmse.index, y = sorted_rmse.rmse)
plt.xticks(rotation=90)
plt.title("RMSE TRAIN SET", size=20)
#save_fig("RMSE TRAIN SET")
plt.show()
In [212]:
plt.figure(figsize=(15,10))
sorted_mae = risultati.sort_values(by = 'mae')
sns.barplot(x = sorted_mae.index, y = sorted_mae.mae)
plt.xticks(rotation=90)
plt.title("MAE TRAIN SET", size=20)
#save_fig("MAE TRAIN SET")
plt.show()
In [213]:
plt.figure(figsize=(15,10))
sorted_r2 = risultati.sort_values(by = 'r2')
sns.barplot(x = sorted_r2.index, y = sorted_r2.r2)
plt.xticks(rotation=90)
plt.title("R2 TRAIN SET", size=20)
#save_fig("R2 TRAIN SET")
plt.show()
In [214]:
plt.figure(figsize=(15,10))
sorted_var = risultati.sort_values(by = 'exp_var')
sns.barplot(x = sorted_var.index, y = sorted_var['exp_var'])
plt.xticks(rotation=90)
plt.title("EXPLAINED VARIANCE TRAIN SET", size=20)
#save_fig("EXPLAINED VARIANCE TRAIN SET")
plt.show()

Da quanto si può osservare nei grafici precedenti il peggiore modello tra tutti (tralasciando i modelli precedentemente esclusi ossia SGD, Lars e Radius Neighbors Regressor) come prestazioni sul train set, a prescindere dalla metrica considerata, risulta essere SVR Regressor, ha infatti elevati valori di root mean squared error e mean absolut error e inoltre per quando riguarda la metrica r2 si notano addirittura valori negativi. I migliori modelli sono senza dubbio quelli facenti parte della categoria di metodi ensemble, infatti il primo modello per risultati migliori è proprio il Gradient Boosting Regressor, seguito poi dal Random Forest Regressor e successivamente dagli altri modelli ensemble che sono stati impiegati nell'analisi. Buoni risultati ci vengono anche forniti dal Multi-Layer Perceptron Regressor e dai modelli ad albero quali Decision Tree Regressor e Extra Tree Regressor, anche se non sono comunque al livello dei precedenti. I peggiori modelli per prestazioni sul train set, oltre ad SVR, rientrano nella categoria dei modelli basati su Support Vector Machine e sul calcolo dei Nearest Neighbours. Infine troviamo nel mezzo la categoria dei modelli lineari con valori di metriche discreti ma comunque superiori rispetto ai modelli ad albero e basati su ANN.

Procediamo ora a consolidare quanto appreso mostrando la media dei valori delle rispettive metriche calcolate per ogni tipologia complessiva (modelli lineari, modelli instance based, ecc..) di modelli addestrati:

In [215]:
aggregati_res_train = ps.DataFrame(index=['Modelli Lineari','Modelli Instance Based','Modelli SVM','Modelli ANN','Modelli ad Albero','Modelli Ensemble'])

linear = risultati.loc[nomi_modelli_lineari]
instance_based = risultati.loc[nomi_modelli_ib]
support_vector = risultati.loc[nomi_modelli_svm]
ann = risultati.loc[nomi_modelli_ann]
tree = risultati.loc[nomi_modelli_tree]
ensemble = risultati.loc[nomi_modelli_ensemble]

rmse_mean = [linear.rmse.mean(),instance_based.rmse.mean(),support_vector.rmse.mean(),ann.rmse.mean(),tree.rmse.mean(),ensemble.rmse.mean()]
mae_mean = [linear.mae.mean(),instance_based.mae.mean(),support_vector.mae.mean(),ann.mae.mean(),tree.mae.mean(),ensemble.mae.mean()]
r2_mean = [linear.r2.mean(),instance_based.r2.mean(),support_vector.r2.mean(),ann.r2.mean(),tree.r2.mean(),ensemble.r2.mean()]
expvar_mean = [linear['exp_var'].mean(),instance_based['exp_var'].mean(),support_vector['exp_var'].mean(),ann['exp_var'].mean(),tree['exp_var'].mean(),ensemble['exp_var'].mean()]

aggregati_res_train['mean_rmse'] = rmse_mean
aggregati_res_train['mean_mae'] = mae_mean
aggregati_res_train['mean_r2'] = r2_mean
aggregati_res_train['mean_expvar'] = expvar_mean

aggregati_res_train
Out[215]:
mean_rmse mean_mae mean_r2 mean_expvar
Modelli Lineari 7.41184221 5.37260302 0.68282033 0.68302042
Modelli Instance Based 10.23343996 7.26734909 0.39503100 0.39891030
Modelli SVM 11.81030301 8.83983083 0.18468615 0.27967373
Modelli ANN 3.85632390 2.78465478 0.91532705 0.91531888
Modelli ad Albero 4.02842329 2.94809076 0.90637045 0.90653447
Modelli Ensemble 2.88085017 2.11710955 0.95209689 0.95216721
In [216]:
plt.figure(figsize=(15,10))
sns.barplot(x = aggregati_res_train.index, y = aggregati_res_train['mean_rmse'])
plt.title("MEDIA RMSE PER TIPO DI MODELLI TRAIN SET", size=20)
#save_fig("MEDIA RMSE TRAIN")
plt.show()
In [217]:
plt.figure(figsize=(15,10))
sns.barplot(x = aggregati_res_train.index, y = aggregati_res_train['mean_mae'])
plt.title("MEDIA MAE PER TIPO DI MODELLI TRAIN SET", size=20)
#save_fig("MEDIA MAE TRAIN")
plt.show()
In [218]:
plt.figure(figsize=(15,10))
sns.barplot(x = aggregati_res_train.index, y = aggregati_res_train['mean_r2'])
plt.title("MEDIA R2 PER TIPO DI MODELLI TRAIN SET", size=20)
#save_fig("MEDIA R2 TRAIN")
plt.show()
In [219]:
plt.figure(figsize=(15,10))
sns.barplot(x = aggregati_res_train.index, y = aggregati_res_train['mean_expvar'])
plt.title("MEDIA EXPLAINED VARIANCE PER TIPO DI MODELLI TRAIN SET", size=20)
#save_fig("MEDIA EXPVAR TRAIN")
plt.show()

Si può appunto sottolineare grazie ai grafici di cui sopra, come i Modelli Ensemble costituiscano la categoria di modelli che raggiungono le prestazioni migliori sul train set rispetto a tutti gli altri, seguiti dai Modelli ANN e successivamente dai Modelli ad ALbero. I peggiori, come già detto precedentemente, risultano essere i Modelli SVM (tra tutti ci restituiscono i valori delle metrice peggiori) ed i Modelli Instance Based; infine per quanto riguarda i Modelli Lineari si riscontrano errori abbastanza elevati ma comunque inferiori alle classi di modelli precedentemente trattate.

APPLICAZIONE E VALUTAZIONE MODELLI SUL TEST SET

Applicazione Modelli sul Test Set

Si procede ora ad applicare i modelli precedentemente addestrati e selezionati sul test set, preparato nelle passate sezioni di tale progetto, questo è stato già trasformato a monte tramite l'utilizzo dell ColumnTransformer e perciò è necessario unicamente effettuare le prevesioni dei valori di metascore. Siccome la funzione precedentemente definita per il calcolo delle metriche sul test set impiega cross-validation, andrebbe a rifittare i modelli sul test set per la restituzione dei valori delle metriche; per questo motivo si procede alla definizione di una nuova funzione "calcola_risultati_test" che non impiega CV. Inoltre dato l'utilizzo di cross-validation per la valutazione delle performance dei modelli ci si aspettano sul test set risultati presumibilmente simili a quelli visti sul training set

In [220]:
from sklearn.metrics import explained_variance_score, mean_absolute_error, mean_squared_error, r2_score
import pickle

ps.options.display.float_format = '{:.8f}'.format

risultati_test = ps.DataFrame(index=nomi,columns=['rmse', 'mae', 'r2', 'exp_var'])

#metodo che passandogli la lista di modelli addestrati sul train set calcola le varie metriche
def calcola_risultati_test(modelli,nomi):
    for i in range(len(modelli)):
        modello = modelli[i]
        y_pred = modello.predict(X_test)
        nome = nomi[i]
        risultati_test.loc[nome,'rmse'] = np.sqrt(mean_squared_error(y_test,y_pred))
        risultati_test.loc[nome,'mae'] = mean_absolute_error(y_test,y_pred)
        risultati_test.loc[nome,'r2'] = r2_score(y_test,y_pred)
        risultati_test.loc[nome,'exp_var'] = explained_variance_score(y_test,y_pred)
    return
In [221]:
#calcola_risultati_test(modelli,nomi)
#risultati_test.to_csv(os.path.join(RESULTS_PATH, "risultati_test.csv"))
risultati_test = ps.read_csv(os.path.join(RESULTS_PATH, "risultati_test.csv"), index_col=[0])
risultati_test
Out[221]:
rmse mae r2 exp_var
Linear Regressor 7.46131931 5.46513181 0.69395983 0.69409208
Ridge Regressor Tuned 7.45826032 5.46142504 0.69421071 0.69433476
ElasticNet Regressor Tuned 7.57462248 5.51696587 0.68459456 0.68476283
Lasso Regressor Tuned 7.46140657 5.46536668 0.69395267 0.69408452
Bayesian Ridge Regressor 7.45803629 5.46147173 0.69422908 0.69435345
KNeighbors Regressor Tuned 10.38667425 7.36037547 0.40693785 0.41626198
LinearSVR Regressor 10.01577967 7.95407813 0.44853658 0.61188424
SVR Regressor 13.70804074 10.35236678 -0.03299439 0.00632455
Multi-layer Perceptron Regressor Tuned 3.59115254 2.64465141 0.92910512 0.93428912
Decision Tree Regressor Tuned 4.02935158 2.96657303 0.91074809 0.91079391
Extra Tree Regressor Tuned 4.02935158 2.96657303 0.91074809 0.91079391
Random Forest Regressor Tuned 2.82723605 2.08733146 0.95605886 0.95605890
Bagging Regressor Tuned 2.82965634 2.10205056 0.95598359 0.95598370
AdaBoost Regressor Tuned 2.91282519 2.11825843 0.95335812 0.95336603
Gradient Boosting Regressor Tuned 2.75431611 2.03657276 0.95829628 0.95829811
Stacking Regressor 2.84912485 2.09467712 0.95537583 0.95537714

Una volta ottenuti i risultati delle metriche per i modelli selezionati nella sezione precedente, si può procedere alla loro visualizzazione ed analisi, a prima vista sembrerebbe che in generale si riscontrino per tutti i modelli dei valori migliori rispetto ai risultati ottenuti sul training set (questo probabilemte dovuto al fatto che precedentemente è stata impiegata cross-validation con la divisione del dataset in 10 fold al fine del calcolo delle metriche di errore).

Valutazione Modelli sul Test Set

In [222]:
plt.figure(figsize=(15,10))
sorted_rmse_test = risultati_test.sort_values(by = 'rmse')
sns.barplot(x = sorted_rmse_test.index, y = sorted_rmse_test.rmse)
plt.xticks(rotation=90)
plt.title("RMSE TEST SET", size=20)
#save_fig("RMSE TEST SET")
plt.show()
In [223]:
plt.figure(figsize=(15,10))
sorted_mae_test = risultati_test.sort_values(by = 'mae')
sns.barplot(x = sorted_mae_test.index, y = sorted_mae_test.mae)
plt.xticks(rotation=90)
plt.title("MAE TEST SET", size=20)
#save_fig("MAE TEST SET")
plt.show()
In [224]:
plt.figure(figsize=(15,10))
sorted_r2_test = risultati_test.sort_values(by = 'r2')
sns.barplot(x = sorted_r2_test.index, y = sorted_r2_test.r2)
plt.xticks(rotation=90)
plt.title("R2 TEST SET", size=20)
#save_fig("R2 TEST SET")
plt.show()
In [225]:
plt.figure(figsize=(15,10))
sorted_var_test = risultati_test.sort_values(by = 'exp_var')
sns.barplot(x = sorted_var_test.index, y = sorted_var_test['exp_var'])
plt.xticks(rotation=90)
plt.title("EXPLAINED VARIANCE TEST SET", size=20)
#save_fig("EXPLAINED VARIANCE TEST SET")
plt.show()

Come detto in precedenza, dato che l'analisi delle metriche sul training set è stata effettuata mediante l'impiego di cross-validation, i risultati ottenuti sul test set restano in linea con quanto osservato fino ad ora. I modelli basati sulle tecniche ensemble risultano essere quelli che restituiscono i risultati migliori, tra tutti il Gradient Boosting Regressor è quello che restituisce previsioni maggiormente corrette, seguito poi dal RandomForest Regressor; i peggiori risultati ci vengono invece restituiti dal SVR Regressor, mentre conservano la stessa posizione sul podio le categorie di modelli lineari, basati su ANN e basati su alberi. Di seguito si può vedere la distribuzione dei valori di metascore predetti dal Gradient Boosting Regressor sul test set mettendola a confronto con gli effettivi valori corretti contenuti in y_test; le curve risultano essere molto simili, si può notare come gli errori si concentrano maggiormente nei pressi del valore '100' di metascore e nel tratto compreso tra '65', circa, ed '80'.

In [229]:
gbr = modelli_ensemble[3]
y_gbr = gbr.predict(X_test)

plt.figure(figsize=(15,10))
sns.kdeplot(data=y_gbr,shade=True,label='valori predetti',color='r')
sns.kdeplot(data=y_test,shade=True,label='valori reali',color='g')
plt.title("Distribuzione valori predetti da GBR", size=20)
#save_fig("distribuzione valori predetti")
plt.show()

Si conclude perciò che mediante l'impiego dei Modelli Ensemble è possibile predire, con una stima dell'errore abbastanza bassa (inferiore al 3%), il valore di metascore per un determinato videogame.